diff --git a/.gitignore b/.gitignore index 028f91e53b..66ff7ab337 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ provision.sh .DS_Store feeds/recent.rss .eslintcache +.svn logs/ @@ -62,3 +63,4 @@ build *.log test/files/normalise.jpg.png test/files/normalise-resized.jpg +package-lock.json diff --git a/Gruntfile.js b/Gruntfile.js index 9a84c15aa0..7fa94e3eaf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,7 +20,7 @@ module.exports = function (grunt) { function update(action, filepath, target) { var updateArgs = args.slice(); - var compiling = ''; + var compiling; var time = Date.now(); if (target === 'lessUpdated_Client') { @@ -37,7 +37,7 @@ module.exports = function (grunt) { // Do nothing, just restart } - if (incomplete.indexOf(compiling) === -1) { + if (compiling && incomplete.indexOf(compiling) === -1) { incomplete.push(compiling); } diff --git a/README.md b/README.md index 3667684b36..43f2b7cb29 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ NodeBB requires the following software to be installed: ## Installation -[Please refer to platform-specific installation documentation](http://docs.nodebb.org/en/latest/installing/os.html) +[Please refer to platform-specific installation documentation](https://docs.nodebb.org/installing/os) ## Securing NodeBB diff --git a/app.js b/app.js index edb8565559..7a0b7bc2a5 100644 --- a/app.js +++ b/app.js @@ -26,7 +26,10 @@ if (require.main !== module) { } var nconf = require('nconf'); -nconf.argv().env('__'); +nconf.argv().env({ + separator: '__', + lowerCase: true, +}); var url = require('url'); var async = require('async'); @@ -90,6 +93,11 @@ if (nconf.get('setup') || nconf.get('install')) { listPlugins(); } else if (nconf.get('build')) { require('./src/meta/build').build(nconf.get('build')); +} else if (nconf.get('events')) { + async.series([ + async.apply(require('./src/database').init), + async.apply(require('./src/events').output), + ]); } else { require('./src/start').start(); } @@ -218,6 +226,7 @@ function upgrade() { function activate() { var db = require('./src/database'); var plugins = require('./src/plugins'); + var events = require('./src/events'); var plugin = nconf.get('activate'); async.waterfall([ function (next) { @@ -238,6 +247,12 @@ function activate() { winston.info('Activating plugin `%s`', plugin); db.sortedSetAdd('plugins:active', 0, plugin, next); }, + function (next) { + events.log({ + type: 'plugin-activate', + text: plugin, + }, next); + }, ], function (err) { if (err) { winston.error(err.message); diff --git a/install/data/footer.json b/install/data/footer.json index 12528110c6..53b2176ade 100644 --- a/install/data/footer.json +++ b/install/data/footer.json @@ -2,7 +2,7 @@ { "widget": "html", "data" : { - "html": "", + "html": "", "title":"", "container":"" } diff --git a/loader.js b/loader.js index 214f785eb9..f897fd79ce 100644 --- a/loader.js +++ b/loader.js @@ -142,7 +142,7 @@ function getPorts() { process.exit(); } var urlObject = url.parse(_url); - var port = nconf.get('port') || nconf.get('PORT') || urlObject.port || 4567; + var port = nconf.get('port') || urlObject.port || 4567; if (!Array.isArray(port)) { port = [port]; } diff --git a/nodebb b/nodebb index 2b71185d4e..45e0ebaec3 100755 --- a/nodebb +++ b/nodebb @@ -495,6 +495,13 @@ var commands = { upgradePlugins(); }, }, + events: { + description: 'Outputs the last ten (10) administrative events recorded by NodeBB', + usage: 'Usage: ' + './nodebb events'.yellow, + handler: function () { + fork(['--events']); + }, + }, help: { description: 'Display the help message for a given command', usage: 'Usage: ' + './nodebb help '.yellow, diff --git a/package.json b/package.json index f19e975b18..766c9c27bb 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "json-2-csv": "^2.0.22", "less": "^2.0.0", "lodash": "^4.17.4", - "lodash.padstart": "^4.6.1", "logrotate-stream": "^0.2.3", "lru-cache": "4.0.2", "mime": "^1.3.4", @@ -56,18 +55,19 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "4.4.10", - "nodebb-plugin-dbsearch": "2.0.2", + "nodebb-plugin-composer-default": "4.4.18", + "nodebb-plugin-dbsearch": "2.0.4", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.2.1", "nodebb-plugin-markdown": "7.1.1", - "nodebb-plugin-mentions": "2.0.3", + "nodebb-plugin-mentions": "2.1.5", "nodebb-plugin-soundpack-default": "1.0.0", "nodebb-plugin-spam-be-gone": "0.5.0", "nodebb-rewards-essentials": "0.0.9", - "nodebb-theme-lavender": "4.0.0", - "nodebb-theme-persona": "5.0.3", - "nodebb-theme-vanilla": "6.0.3", + "nodebb-theme-lavender": "4.0.4", + "nodebb-theme-persona": "5.0.11", + "nodebb-theme-slick": "1.1.0", + "nodebb-theme-vanilla": "6.0.8", "nodebb-widget-essentials": "3.0.0", "nodemailer": "2.6.4", "nodemailer-sendmail-transport": "1.0.0", diff --git a/public/language/ar/admin/appearance/customise.json b/public/language/ar/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/ar/admin/appearance/customise.json +++ b/public/language/ar/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/ar/admin/development/info.json b/public/language/ar/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/ar/admin/development/info.json +++ b/public/language/ar/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/ar/admin/general/languages.json b/public/language/ar/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/ar/admin/general/languages.json +++ b/public/language/ar/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/ar/admin/manage/categories.json b/public/language/ar/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/ar/admin/manage/categories.json +++ b/public/language/ar/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/ar/admin/settings/user.json b/public/language/ar/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/ar/admin/settings/user.json +++ b/public/language/ar/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 0a05be7777..68de3c7528 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -1,5 +1,6 @@ { "invalid-data": "بيانات غير صحيحة", + "invalid-json": "Invalid JSON", "not-logged-in": "لم تقم بتسجيل الدخول", "account-locked": "تم حظر حسابك مؤقتًا.", "search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل", @@ -12,6 +13,7 @@ "invalid-title": "عنوان غير صحيح", "invalid-user-data": "بيانات المستخدم غير صحيحة", "invalid-password": "كلمة السر غير مقبولة", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "المرجود تحديد اسم مستخدم و كلمة مرور", "invalid-search-term": "كلمة البحث غير صحيحة", "csrf-invalid": "لم تتمكن من تسجيل الدخول. هنالك أحتمال ان جلستك انتهت. رجاءًا حاول مرة اخرى.", diff --git a/public/language/ar/global.json b/public/language/ar/global.json index 70ac3ba171..73e23bfb28 100644 --- a/public/language/ar/global.json +++ b/public/language/ar/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/ar/topic.json b/public/language/ar/topic.json index 34c6750ea8..4a5c8789b0 100644 --- a/public/language/ar/topic.json +++ b/public/language/ar/topic.json @@ -14,6 +14,7 @@ "quote": "اقتبس", "reply": "رد", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "رد بموضوع", "guest-login-reply": "يجب عليك تسجيل الدخول للرد", @@ -58,6 +59,7 @@ "thread_tools.unlock": "إلغاء إقفال الموضوع", "thread_tools.move": "نقل الموضوع", "thread_tools.move_all": "نقل الكل", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "إنشاء فرع الموضوع", "thread_tools.delete": "حذف الموضوع", "thread_tools.delete-posts": "مشاركات محذوفة", @@ -71,7 +73,6 @@ "post_restore_confirm": "هل أنت متأكد أنك تريد استعادة هذه المشاركة؟", "post_purge_confirm": "هل أنت متأكد أنك تريد تطهير هذه المشاركة؟", "load_categories": "تحميل الفئات", - "disabled_categories_note": "الفئات المعطلة رمادية", "confirm_move": "انقل", "confirm_fork": "فرع", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "نقل المشاركة", "post_moved": "تم نقل المشاركة", "fork_topic": "فرع الموضوع", - "topic_will_be_moved_to": "هذا الموضوع سوف ينقل إلى فئة", "fork_topic_instruction": "إضغط على المشاركات التي تريد تفريعها", "fork_no_pids": "لم تختر أي مشاركة", "fork_pid_count": "1% مشاركة محددة", diff --git a/public/language/bg/admin/appearance/customise.json b/public/language/bg/admin/appearance/customise.json index 2c5b67e504..f052e4e5c9 100644 --- a/public/language/bg/admin/appearance/customise.json +++ b/public/language/bg/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Персонализирана заглавна част", "custom-header.description": "Въведете своя персонализиран код HTML тук (напр. JavaScript, елементи „meta“ и т.н.), те ще бъдат добавени към секцията <head> в кода на Вашия форум.", - "custom-header.enable": "Включване на персонализирана заглавна част" + "custom-header.enable": "Включване на персонализирана заглавна част", + + "custom-css.livereload": "Включване на моменталното презареждане", + "custom-css.livereload.description": "Ако включите това, всички сесии на всяко устройство, където използвате акаунта си, ще се презареждат, когато натискате „Запазване“." } \ No newline at end of file diff --git a/public/language/bg/admin/development/info.json b/public/language/bg/admin/development/info.json index cfd2990662..87ff0b8b70 100644 --- a/public/language/bg/admin/development/info.json +++ b/public/language/bg/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Информация — Вие сте на %1:%2", + "nodes-responded": "%1 възела отговориха в рамките на %2мсек!", "host": "сървър", "pid": "ид. на процеса", "nodejs": "nodejs", "online": "на линия", "git": "git", + "memory": "памет", "load": "натоварване", "uptime": "активно време", diff --git a/public/language/bg/admin/general/languages.json b/public/language/bg/admin/general/languages.json index 2ccfa73419..b4dbba6c3e 100644 --- a/public/language/bg/admin/general/languages.json +++ b/public/language/bg/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Езикови настройки", "description": "Езикът по подразбиране определя езиковите настройки за всички потребители, които посещават Вашия форум.
Отделните потребители могат да сменят езика си от страницата с настройки на профила си.", - "default-language": "Език по подразбиране" + "default-language": "Език по подразбиране", + "auto-detect": "Автоматично разпознаване на езика за гостите" } \ No newline at end of file diff --git a/public/language/bg/admin/manage/categories.json b/public/language/bg/admin/manage/categories.json index 934bf262ff..bd503559f6 100644 --- a/public/language/bg/admin/manage/categories.json +++ b/public/language/bg/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Персонализиран клас", "num-recent-replies": "Брой на скорошните отговори", "ext-link": "Външна връзка", + "is-section": "Използване на тази категория като раздел", "upload-image": "Качване на изображение", "delete-image": "Премахване", "category-image": "Изображение на категорията", diff --git a/public/language/bg/admin/settings/user.json b/public/language/bg/admin/settings/user.json index a470c905d0..294ee7939a 100644 --- a/public/language/bg/admin/settings/user.json +++ b/public/language/bg/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Максимален брой покани на потребител", "max-invites": "Максимален брой покани на потребител", "max-invites-help": "0 = няма ограничение. Администраторите могат да разпращат неограничен брой покани.
Тази стойност се използва, само ако е избран режимът „Само с покана“.", + "invite-expiration": "Давност на поканите", + "invite-expiration-help": "Брой дни, след които поканите вече не важат.", "min-username-length": "Минимална дължина на потребителското име", "max-username-length": "Максимална дължина на потребителското име", "min-password-length": "Минимална дължина на паролата", diff --git a/public/language/bg/error.json b/public/language/bg/error.json index 6733bdcca5..1251e2d864 100644 --- a/public/language/bg/error.json +++ b/public/language/bg/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Грешни данни", + "invalid-json": "Неправилен JSON", "not-logged-in": "Изглежда не сте се вписали в системата.", "account-locked": "Вашият акаунт беше заключен временно", "search-requires-login": "Търсенето изисква регистриран акаунт! Моля, впишете се или се регистрирайте!", @@ -12,6 +13,7 @@ "invalid-title": "Грешно заглавие!", "invalid-user-data": "Грешни потребителски данни", "invalid-password": "Грешна парола", + "invalid-login-credentials": "Неправилни данни за удостоверяване", "invalid-username-or-password": "Моля, въведете потребителско име и парола", "invalid-search-term": "Грешен текст за търсене", "csrf-invalid": "Не успяхме да Ви впишем, най-вероятно защото сесията Ви е изтекла. Моля, опитайте отново", diff --git a/public/language/bg/global.json b/public/language/bg/global.json index 7a535a1bab..801f82740c 100644 --- a/public/language/bg/global.json +++ b/public/language/bg/global.json @@ -103,5 +103,7 @@ "cookies.message": "Този уеб сайт използва бисквитки, за да предостави услугите си по възможно най-добрия начин.", "cookies.accept": "Разбрано!", "cookies.learn_more": "Научете повече", - "edited": "Редактирано" + "edited": "Редактирано", + "disabled": "Изключено", + "select": "Избиране" } \ No newline at end of file diff --git a/public/language/bg/topic.json b/public/language/bg/topic.json index 4f0827c267..fb7faaea2f 100644 --- a/public/language/bg/topic.json +++ b/public/language/bg/topic.json @@ -14,6 +14,7 @@ "quote": "Цитат", "reply": "Отговор", "replies_to_this_post": "%1 отговора", + "one_reply_to_this_post": "1 отговор", "last_reply_time": "Последен отговор", "reply-as-topic": "Отговор в нова тема", "guest-login-reply": "Впишете се, за да отговорите", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Отключване на темата", "thread_tools.move": "Преместване на темата", "thread_tools.move_all": "Преместване на всички", + "thread_tools.select_category": "Избиране на категория", "thread_tools.fork": "Разделяне на темата", "thread_tools.delete": "Изтриване на темата", "thread_tools.delete-posts": "Изтриване на публикациите", @@ -71,7 +73,6 @@ "post_restore_confirm": "Наистина ли искате да възстановите тази публикация?", "post_purge_confirm": "Наистина ли искате да изчистите тази публикация?", "load_categories": "Зареждане на категориите", - "disabled_categories_note": "Изключените категории са засивени", "confirm_move": "Преместване", "confirm_fork": "Разделяне", "bookmark": "Отметка", @@ -83,7 +84,6 @@ "move_post": "Преместване на публикацията", "post_moved": "Публикацията беше преместена!", "fork_topic": "Разделяне на темата", - "topic_will_be_moved_to": "Тази тема ще бъде преместена в категорията", "fork_topic_instruction": "Натиснете публикациите, които искате да отделите", "fork_no_pids": "Няма избрани публикации!", "fork_pid_count": "Избрани публикации: %1", diff --git a/public/language/bn/admin/appearance/customise.json b/public/language/bn/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/bn/admin/appearance/customise.json +++ b/public/language/bn/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/bn/admin/development/info.json b/public/language/bn/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/bn/admin/development/info.json +++ b/public/language/bn/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/bn/admin/general/languages.json b/public/language/bn/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/bn/admin/general/languages.json +++ b/public/language/bn/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/bn/admin/manage/categories.json b/public/language/bn/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/bn/admin/manage/categories.json +++ b/public/language/bn/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/bn/admin/settings/user.json b/public/language/bn/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/bn/admin/settings/user.json +++ b/public/language/bn/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 5c97eecb55..b97a0ff8ff 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -1,5 +1,6 @@ { "invalid-data": "ভুল তথ্য", + "invalid-json": "Invalid JSON", "not-logged-in": "আপনি লগিন করেননি", "account-locked": "আপনার অ্যাকাউন্ট সাময়িকভাবে লক করা হয়েছে", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "ভুল শিরোনাম", "invalid-user-data": "ভুল ব্যবহারকারী তথ্য", "invalid-password": "ভুল পাসওয়ার্ড", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "অনুগ্রহ পূর্বক ইউজারনেম এবং পাসওয়ার্ড উভয়ই প্রদান করুন", "invalid-search-term": "অগ্রহনযোগ্য সার্চ টার্ম", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/bn/global.json b/public/language/bn/global.json index 09504ab08b..7741b7b2a8 100644 --- a/public/language/bn/global.json +++ b/public/language/bn/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/bn/topic.json b/public/language/bn/topic.json index 66863ac28b..46b6c48e0f 100644 --- a/public/language/bn/topic.json +++ b/public/language/bn/topic.json @@ -14,6 +14,7 @@ "quote": "উদ্ধৃতি", "reply": "উত্তর", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", @@ -58,6 +59,7 @@ "thread_tools.unlock": "টপিক খুলে দিন", "thread_tools.move": "টপিক সরান", "thread_tools.move_all": "সমস্ত টপিক সরান", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "টপিক ফর্ক করুন", "thread_tools.delete": "টপিক মুছে ফেলুন", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "আপনি নিশ্চিত যে আপনি এই পোষ্টটি পুনরূূদ্ধার করতে চান ? ", "post_purge_confirm": "আপনি নিশ্চিত যে আপনি এই পোষ্টটি পার্জ করতে চান ? ", "load_categories": "ক্যাটাগরী লোড করা হচ্ছে", - "disabled_categories_note": "নিস্ক্রীয় ক্যাটাগরীসমূহ ধূসর কালিতে লেখা রয়েছে। ", "confirm_move": "সরান", "confirm_fork": "ফর্ক", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "পোষ্ট সরান", "post_moved": "পোষ্ট সরানো হয়েছে", "fork_topic": "টপিক ফর্ক করুন", - "topic_will_be_moved_to": "এই টপিকটি ক্যাটাগরীতে সরানো হবে", "fork_topic_instruction": "যে পোষ্টটি ফর্ক করতে চান সেটি ক্লিক করুন", "fork_no_pids": "কোন পোষ্ট সিলেক্ট করা হয় নি", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/cs/admin/appearance/customise.json b/public/language/cs/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/cs/admin/appearance/customise.json +++ b/public/language/cs/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/cs/admin/development/info.json b/public/language/cs/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/cs/admin/development/info.json +++ b/public/language/cs/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/cs/admin/general/languages.json b/public/language/cs/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/cs/admin/general/languages.json +++ b/public/language/cs/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/cs/admin/manage/categories.json b/public/language/cs/admin/manage/categories.json index a6702766ce..6d9b9f161b 100644 --- a/public/language/cs/admin/manage/categories.json +++ b/public/language/cs/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# nedávných odpovědí", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/cs/admin/settings/user.json b/public/language/cs/admin/settings/user.json index c1684da402..83d3308671 100644 --- a/public/language/cs/admin/settings/user.json +++ b/public/language/cs/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimální délka uživatelského jména", "max-username-length": "Maximální délka uživatelského jména", "min-password-length": "Minimální délka hesla", diff --git a/public/language/cs/error.json b/public/language/cs/error.json index 41ca155c7a..628b1c9109 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Neplatná data", + "invalid-json": "Invalid JSON", "not-logged-in": "Zdá se, že nejste přihlášen(a)", "account-locked": "Váš účet byl dočasně uzamčen", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Neplatný titulek!", "invalid-user-data": "Neplatná uživatelská data", "invalid-password": "Neplatné heslo", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Stanovte, prosím, oboje, jak uživatelské jméno, tak heslo", "invalid-search-term": "Neplatný výraz pro vyhledávání", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/cs/global.json b/public/language/cs/global.json index 3d6a1fe149..143f909755 100644 --- a/public/language/cs/global.json +++ b/public/language/cs/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/cs/topic.json b/public/language/cs/topic.json index 67a774b3e8..98429c8ad0 100644 --- a/public/language/cs/topic.json +++ b/public/language/cs/topic.json @@ -14,6 +14,7 @@ "quote": "Citovat", "reply": "Odpovědět", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Odpovědět jako Téma", "guest-login-reply": "Přihlásit se pro odpověď", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Odemknout téma", "thread_tools.move": "Přesunout téma", "thread_tools.move_all": "Přesunout vše", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Větvit téma", "thread_tools.delete": "Odstranit téma", "thread_tools.delete-posts": "Odstranit přispěvky", @@ -71,7 +73,6 @@ "post_restore_confirm": "Are you sure you want to restore this post?", "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Načítání kategorií", - "disabled_categories_note": "Vypnuté (disabled) kategorie jsou šedé.", "confirm_move": "Přesunout", "confirm_fork": "Rozdělit", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Přesunout příspěvek", "post_moved": "Příspěvek přesunut!", "fork_topic": "Rozdělit příspěvek", - "topic_will_be_moved_to": "Toto téma bude přesunuto do kategorie", "fork_topic_instruction": "Vyber příspěvky, které chceš oddělit", "fork_no_pids": "Žádné příspěvky nebyly vybrány!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/da/admin/appearance/customise.json b/public/language/da/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/da/admin/appearance/customise.json +++ b/public/language/da/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/da/admin/development/info.json b/public/language/da/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/da/admin/development/info.json +++ b/public/language/da/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/da/admin/general/languages.json b/public/language/da/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/da/admin/general/languages.json +++ b/public/language/da/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/da/admin/manage/categories.json b/public/language/da/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/da/admin/manage/categories.json +++ b/public/language/da/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/da/admin/settings/user.json b/public/language/da/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/da/admin/settings/user.json +++ b/public/language/da/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/da/error.json b/public/language/da/error.json index 271adb8fd3..c2419381c8 100644 --- a/public/language/da/error.json +++ b/public/language/da/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Ugyldig Data", + "invalid-json": "Invalid JSON", "not-logged-in": "Det ser ikke ud til at du er logget ind.", "account-locked": "Din konto er blevet blokeret midlertidigt.", "search-requires-login": "Du skal have en konto for at søge - log venligst ind eller registrer dig.", @@ -12,6 +13,7 @@ "invalid-title": "Ugylidt titel", "invalid-user-data": "Ugyldig Bruger Data", "invalid-password": "Ugyldig Adgangskode", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Venligst angiv både brugernavn og adgangskode", "invalid-search-term": "Ugyldig søgeterm", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/da/global.json b/public/language/da/global.json index 211aa200bd..311cbe9705 100644 --- a/public/language/da/global.json +++ b/public/language/da/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/da/topic.json b/public/language/da/topic.json index 54d4114614..de6b087b5d 100644 --- a/public/language/da/topic.json +++ b/public/language/da/topic.json @@ -14,6 +14,7 @@ "quote": "Citer", "reply": "Svar", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Svar som emne", "guest-login-reply": "Login for at svare", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Lås tråd op", "thread_tools.move": "Flyt tråd", "thread_tools.move_all": "Flyt alt", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Fraskil tråd", "thread_tools.delete": "Slet tråd", "thread_tools.delete-posts": "Slet Indlæg", @@ -71,7 +73,6 @@ "post_restore_confirm": "Er du sikker på at du vil gendanne dette indlæg?", "post_purge_confirm": "Er du sikker på at du vil udradere dette indlæg?", "load_categories": "Indlæser kategorier", - "disabled_categories_note": "Deaktiverede kategorier er nedtonede", "confirm_move": "Flyt", "confirm_fork": "Fraskil", "bookmark": "Bogmærke", @@ -83,7 +84,6 @@ "move_post": "Flyt indlæg", "post_moved": "Indlæg flyttet!", "fork_topic": "Fraskil tråd", - "topic_will_be_moved_to": "Denne tråd vil blive flyttet til katagorien", "fork_topic_instruction": "Klik på indlæg du ønsker at fraskille", "fork_no_pids": "Ingen indlæg valgt", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/de/admin/advanced/database.json b/public/language/de/admin/advanced/database.json index a2b3963632..7caa65a1bb 100644 --- a/public/language/de/admin/advanced/database.json +++ b/public/language/de/admin/advanced/database.json @@ -1,7 +1,7 @@ { "x-b": "%1 B", "x-mb": "%1 MB", - "x-gb": "%1 gb", + "x-gb": "%1 GB", "uptime-seconds": "Uptime in Sekunden", "uptime-days": "Uptime in Tagen", diff --git a/public/language/de/admin/appearance/customise.json b/public/language/de/admin/appearance/customise.json index 426750609f..657fe93ea8 100644 --- a/public/language/de/admin/appearance/customise.json +++ b/public/language/de/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Benutzerdefinierter Header", "custom-header.description": "Füge hier dein benutzerdefiniertes HTML (z.B. Javascript, Meta Tags, usw.) ein, welches in den <head> Tag eingefügt werden soll.", - "custom-header.enable": "Benutzerdefinierten Header aktivieren" + "custom-header.enable": "Benutzerdefinierten Header aktivieren", + + "custom-css.livereload": "Live-Aktualisierung aktivieren", + "custom-css.livereload.description": "Aktiviere diese Einstellung um alle Sessions auf allen Geräten mit deinem Account dazu zu zwingen Neuzulaladen wenn du Speichern drückst" } \ No newline at end of file diff --git a/public/language/de/admin/development/info.json b/public/language/de/admin/development/info.json index 810511ae04..56e27b7e72 100644 --- a/public/language/de/admin/development/info.json +++ b/public/language/de/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - Sie verwenden %1:%2", + "nodes-responded": "%1 Knoten antworteten innerhalb von %2ms", "host": "Host", "pid": "PID", "nodejs": "Node.js Version", "online": "Online", "git": "git", + "memory": "Speicher", "load": "Auslastung", "uptime": "Uptime", diff --git a/public/language/de/admin/general/dashboard.json b/public/language/de/admin/general/dashboard.json index 79e3006735..c70a86e3fc 100644 --- a/public/language/de/admin/general/dashboard.json +++ b/public/language/de/admin/general/dashboard.json @@ -5,14 +5,14 @@ "users": "Benutzer", "posts": "Beiträge", "topics": "Themen", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", + "page-views-seven": "Letzte 7 Tage", + "page-views-thirty": "Letzte 30 Tage", + "page-views-last-day": "Letzte 24 Stunden", + "page-views-custom": "Benutzerdefinierte Tagesspanne", + "page-views-custom-start": "Spannen-Anfang", + "page-views-custom-end": "Spannen-Ende", + "page-views-custom-help": "Gib eine Zeitspanne an, in dem du die Besichtigungszahlen ansehen willst. Sollte keine Kalenderauswahl verfügbar sein ist das akzeptierte format YYYY-MM-DD", + "page-views-custom-error": "Bitte gib eine gültige Zeitspanne im Format YYYY-MM-DD an", "stats.day": "Diesen Tag", "stats.week": "Diese Woche", @@ -23,8 +23,8 @@ "running-version": "Es läuft NodeBB v%1.", "keep-updated": "Stelle sicher, dass dein NodeBB immer auf dem neuesten Stand für die neuesten Sicherheits-Patches und Bug-fixes ist.", "up-to-date": "

NodeBB Version ist aktuell

", - "upgrade-available": "

Version (v%1) wurde veröffentlicht. Es wird ein NodeBB Upgrade empfohlen.

", - "prerelease-upgrade-available": "

Das ist eine veraltete pre-release Version von NodeBB. Version (v%1) wurde veröffentlicht. Es wird ein NodeBB Upgrade empfohlen.

", + "upgrade-available": "

Version (v%1) wurde veröffentlicht. Ein NodeBB Upgrade wird empfohlen.

", + "prerelease-upgrade-available": "

Das ist eine veraltete pre-release Version von NodeBB. Version (v%1) wurde veröffentlicht. Ein NodeBB Upgrade wird empfohlen.

", "prerelease-warning": "

Das ist eine pre-release Version von NodeBB. Es können ungewollte Fehler auftreten.

", "running-in-development": "Das Forum wurde im Entwicklermodus gestartet. Das Forum könnte potenziellen Gefahren ausgeliefert sein. Bitte kontaktiere den Systemadministrator.", diff --git a/public/language/de/admin/general/languages.json b/public/language/de/admin/general/languages.json index af0bb78d1f..c2358ac047 100644 --- a/public/language/de/admin/general/languages.json +++ b/public/language/de/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Spracheinstellungen", "description": "Die Standardsprache legt die Spracheinstellungen für alle Benutzer fest, die das Forum besuchen.
Einzelne Benutzer können die Standardsprache auf der Seite in ihren Kontoeinstellungen überschreiben.", - "default-language": "Standardsprache" + "default-language": "Standardsprache", + "auto-detect": "Sprach-Einstellung bei Gästen automatisch ermitteln" } \ No newline at end of file diff --git a/public/language/de/admin/manage/categories.json b/public/language/de/admin/manage/categories.json index 0bc48e206c..74ad8d0e9b 100644 --- a/public/language/de/admin/manage/categories.json +++ b/public/language/de/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Benutzderdefinierte Klasse", "num-recent-replies": "Anzahl neuer Antworten", "ext-link": "Externer Link", + "is-section": "Behandle diese Kategorie als Abschnitt", "upload-image": "Bild hochladen", "delete-image": "Entfernen", "category-image": "Kategoriebild", diff --git a/public/language/de/admin/settings/post.json b/public/language/de/admin/settings/post.json index 5844dfe9eb..ab310f421e 100644 --- a/public/language/de/admin/settings/post.json +++ b/public/language/de/admin/settings/post.json @@ -29,8 +29,8 @@ "unread": "Ungelesen-Einstellungen", "unread.cutoff": "Ungelesen-Limit (in Tagen)", "unread.min-track-last": "Minimale Anzahl an Beiträgen pro Thema bevor die letzte Sichtung mitgeschrieben wird", - "recent": "Recent Settings", - "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", + "recent": "Kürzlich verwendete Einstellungen", + "recent.categoryFilter.disable": "Filtern von Themen in ignorierten Kategorien auf der /recent Seite deaktivieren", "signature": "Signatureinstellungen", "signature.disable": "Signaturen deaktivieren", "signature.no-links": "Links in signaturen deaktivieren", diff --git a/public/language/de/admin/settings/user.json b/public/language/de/admin/settings/user.json index b5bfb291c9..e7d7e34d1c 100644 --- a/public/language/de/admin/settings/user.json +++ b/public/language/de/admin/settings/user.json @@ -34,10 +34,12 @@ "registration.max-invites": "Maximale Einladungen pro Benutzer", "max-invites": "Maximale Einladungen pro Benutzer", "max-invites-help": "0 für keine Beschränkung. Admins haben keine beschränkung.
Nur praktikabel für \"Nur Einladungen\".", + "invite-expiration": "Einladungsfrist", + "invite-expiration-help": "# der Tage nachdem Einladungen auslaufen.", "min-username-length": "Minimale länge des Benutzernamens", "max-username-length": "Maximale länge des Benutzernamens", "min-password-length": "Minimale länge des Passwortes", - "min-password-strength": "Minimum Password Strength", + "min-password-strength": "Minimale Passwort stärke", "max-about-me-length": "Maximale länge von Über Mich", "terms-of-use": "Forum Nutzungsbedingungen (Leer lassen um es zu deaktivieren)", "user-search": "Benutzersuche", diff --git a/public/language/de/email.json b/public/language/de/email.json index 1ade727750..dcd3eb4fa3 100644 --- a/public/language/de/email.json +++ b/public/language/de/email.json @@ -34,7 +34,7 @@ "unsub.cta": "Klicke hier, um diese Einstellungen zu ändern.", "banned.subject": "Du wurdest von %1 gebannt.", "banned.text1": "Der Benutzer %1 wurde von %2 gebannt.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.text2": "Dieser Bann wird bis %1 dauern.", + "banned.text3": "Diese ist der Grund weshalb du gebannt wurdest", "closing": "Danke!" } \ No newline at end of file diff --git a/public/language/de/error.json b/public/language/de/error.json index 51526a9041..32b5fc4388 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Ungültige Daten", + "invalid-json": "Ungültiges JSON", "not-logged-in": "Du bist nicht angemeldet.", "account-locked": "Dein Account wurde vorübergehend gesperrt.", "search-requires-login": "Die Suche erfordert ein Konto, bitte einloggen oder registrieren.", @@ -12,6 +13,7 @@ "invalid-title": "Ungültiger Titel", "invalid-user-data": "Ungültige Benutzerdaten", "invalid-password": "Ungültiges Passwort", + "invalid-login-credentials": "Ungültige Zugangsdaten", "invalid-username-or-password": "Bitte gebe einen Benutzernamen und ein Passwort an", "invalid-search-term": "Ungültige Suchanfrage", "csrf-invalid": "Dein Login war nicht erfolgreich da wahrscheinlich deine Sitzung abgelaufen ist. Bitte versuche es noch einmal", diff --git a/public/language/de/global.json b/public/language/de/global.json index 98a65c6687..d9d0a7eb3e 100644 --- a/public/language/de/global.json +++ b/public/language/de/global.json @@ -103,5 +103,7 @@ "cookies.message": "Diese Website verwendet Cookies, um sicherzustellen, dass du die besten Erfahrungen auf unserer Website machst.", "cookies.accept": "Verstanden!", "cookies.learn_more": "Erfahre mehr", - "edited": "Bearbeitet" + "edited": "Bearbeitet", + "disabled": "Deaktiviert", + "select": "Auswählen" } \ No newline at end of file diff --git a/public/language/de/topic.json b/public/language/de/topic.json index 76861f0d00..f39f464fa1 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -14,6 +14,7 @@ "quote": "Zitieren", "reply": "Antworten", "replies_to_this_post": "%1 Antworten", + "one_reply_to_this_post": "1 Antwort", "last_reply_time": "Letzte Antwort", "reply-as-topic": "In einem neuen Thema antworten", "guest-login-reply": "Anmelden zum Antworten", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Thema öffnen", "thread_tools.move": "Thema verschieben", "thread_tools.move_all": "Alle verschieben", + "thread_tools.select_category": "Kategorie auswählen", "thread_tools.fork": "Thema aufspalten", "thread_tools.delete": "Thema löschen", "thread_tools.delete-posts": "Beiträge entfernen", @@ -71,7 +73,6 @@ "post_restore_confirm": "Sind Sie sicher, dass Sie diesen Beitrag wiederherstellen möchten?", "post_purge_confirm": "Sind Sie sicher, das Sie diesen Beitrag bereinigen möchten?", "load_categories": "Kategorien laden", - "disabled_categories_note": "Deaktivierte Kategorien sind ausgegraut.", "confirm_move": "Verschieben", "confirm_fork": "Aufspalten", "bookmark": "Lesezeichen", @@ -83,7 +84,6 @@ "move_post": "Beitrag verschieben", "post_moved": "Beitrag wurde verschoben!", "fork_topic": "Thema aufspalten", - "topic_will_be_moved_to": "Dieses Thema wird verschoben nach", "fork_topic_instruction": "Klicke auf die Beiträge, die aufgespaltet werden sollen", "fork_no_pids": "Keine Beiträge ausgewählt!", "fork_pid_count": "%1 Beiträge ausgewählt", diff --git a/public/language/el/admin/appearance/customise.json b/public/language/el/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/el/admin/appearance/customise.json +++ b/public/language/el/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/el/admin/development/info.json b/public/language/el/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/el/admin/development/info.json +++ b/public/language/el/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/el/admin/general/languages.json b/public/language/el/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/el/admin/general/languages.json +++ b/public/language/el/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/el/admin/manage/categories.json b/public/language/el/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/el/admin/manage/categories.json +++ b/public/language/el/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/el/admin/settings/user.json b/public/language/el/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/el/admin/settings/user.json +++ b/public/language/el/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/el/error.json b/public/language/el/error.json index cb553a38d1..1f222177af 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Άκυρα Δεδομένα", + "invalid-json": "Invalid JSON", "not-logged-in": "Φαίνεται πως δεν είσαι συνδεδεμένος/η.", "account-locked": "Ο λογαριασμός σου έχει κλειδωθεί προσωρινά", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Άκυρος Τίτλος!", "invalid-user-data": "Άκυρα Δεδομένα Χρήστη", "invalid-password": "Άκυρος Κωδικός", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Παρακαλώ γράψε το όνομα χρήστη και τον κωδικό", "invalid-search-term": "Άκυρος όρος αναζήτησης", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/el/global.json b/public/language/el/global.json index 3a98632abf..2a8d6f7ee3 100644 --- a/public/language/el/global.json +++ b/public/language/el/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Μάθε Περισσότερα", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/el/topic.json b/public/language/el/topic.json index 0d382c2407..b2e35c7484 100644 --- a/public/language/el/topic.json +++ b/public/language/el/topic.json @@ -14,6 +14,7 @@ "quote": "Παράθεση", "reply": "Απάντηση", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Ξεκλείδωμα Θέματος", "thread_tools.move": "Μετακίνηση Θέματος", "thread_tools.move_all": "Μετακίνηση Όλων", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Διαχωρισμός Θέματος", "thread_tools.delete": "Διαγραφή Θέματος", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Είσαι σίγουρος/η πως θέλεις να επαναφέρεις αυτή την δημοσίευση;", "post_purge_confirm": "Είσαι σίγουρος/η πως θέλεις να εκκαθαρίσεις αυτή την δημοσίευση;", "load_categories": "Οι Κατηγορίες Φορτώνουν", - "disabled_categories_note": "Οι απενεργοποιημένες κατηγορίες είναι γκριζαρισμένες", "confirm_move": "Μετακίνηση", "confirm_fork": "Διαχωρισμός", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Μετακίνηση Δημοσίευσης", "post_moved": "Η δημοσίευση μετακινήθηκε!", "fork_topic": "Διαχωρισμός Θέματος", - "topic_will_be_moved_to": "Το θέμα θα μετακινηθεί στην κατηγορία", "fork_topic_instruction": "Κάνε κλικ στις δημοσιεύσεις που θέλεις να διαχωρίσεις", "fork_no_pids": "Δεν έχουν επιλεχθεί δημοσιεύσεις!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/en-GB/admin/manage/categories.json b/public/language/en-GB/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/en-GB/admin/manage/categories.json +++ b/public/language/en-GB/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/en-GB/global.json b/public/language/en-GB/global.json index bb152e4561..741fb4d2b8 100644 --- a/public/language/en-GB/global.json +++ b/public/language/en-GB/global.json @@ -130,6 +130,8 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - - "edited": "Edited" + + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 727417ca5f..d206960156 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -70,6 +70,7 @@ "thread_tools.unlock": "Unlock Topic", "thread_tools.move": "Move Topic", "thread_tools.move_all": "Move All", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Delete Topic", "thread_tools.delete-posts": "Delete Posts", @@ -86,7 +87,6 @@ "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Loading Categories", - "disabled_categories_note": "Disabled Categories are greyed out", "confirm_move": "Move", "confirm_fork": "Fork", @@ -100,7 +100,6 @@ "move_post": "Move Post", "post_moved": "Post moved!", "fork_topic": "Fork Topic", - "topic_will_be_moved_to": "This topic will be moved to the category", "fork_topic_instruction": "Click the posts you want to fork", "fork_no_pids": "No posts selected!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/en-US/admin/appearance/customise.json b/public/language/en-US/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/en-US/admin/appearance/customise.json +++ b/public/language/en-US/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/en-US/admin/development/info.json b/public/language/en-US/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/en-US/admin/development/info.json +++ b/public/language/en-US/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/en-US/admin/general/languages.json b/public/language/en-US/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/en-US/admin/general/languages.json +++ b/public/language/en-US/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/en-US/admin/manage/categories.json b/public/language/en-US/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/en-US/admin/manage/categories.json +++ b/public/language/en-US/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/en-US/admin/settings/user.json b/public/language/en-US/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/en-US/admin/settings/user.json +++ b/public/language/en-US/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/en-US/error.json b/public/language/en-US/error.json index 35eaf8cbc6..403c0b4aa7 100644 --- a/public/language/en-US/error.json +++ b/public/language/en-US/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Invalid Data", + "invalid-json": "Invalid JSON", "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Invalid title!", "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Please specify both a username and password", "invalid-search-term": "Invalid search term", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/en-US/global.json b/public/language/en-US/global.json index c3522f80ac..d9255ae0f5 100644 --- a/public/language/en-US/global.json +++ b/public/language/en-US/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/en-US/topic.json b/public/language/en-US/topic.json index 519cc5cd74..9c41963d50 100644 --- a/public/language/en-US/topic.json +++ b/public/language/en-US/topic.json @@ -14,6 +14,7 @@ "quote": "Quote", "reply": "Reply", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Unlock Topic", "thread_tools.move": "Move Topic", "thread_tools.move_all": "Move All", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Delete Topic", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Are you sure you want to restore this post?", "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Loading Categories", - "disabled_categories_note": "Disabled Categories are greyed out", "confirm_move": "Move", "confirm_fork": "Fork", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Move Post", "post_moved": "Post moved!", "fork_topic": "Fork Topic", - "topic_will_be_moved_to": "This topic will be moved to the category", "fork_topic_instruction": "Click the posts you want to fork", "fork_no_pids": "No posts selected!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/en-x-pirate/admin/appearance/customise.json b/public/language/en-x-pirate/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/en-x-pirate/admin/appearance/customise.json +++ b/public/language/en-x-pirate/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/development/info.json b/public/language/en-x-pirate/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/en-x-pirate/admin/development/info.json +++ b/public/language/en-x-pirate/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/en-x-pirate/admin/general/languages.json b/public/language/en-x-pirate/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/en-x-pirate/admin/general/languages.json +++ b/public/language/en-x-pirate/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/manage/categories.json b/public/language/en-x-pirate/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/en-x-pirate/admin/manage/categories.json +++ b/public/language/en-x-pirate/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/en-x-pirate/admin/settings/user.json b/public/language/en-x-pirate/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/en-x-pirate/admin/settings/user.json +++ b/public/language/en-x-pirate/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/en-x-pirate/error.json b/public/language/en-x-pirate/error.json index 35eaf8cbc6..403c0b4aa7 100644 --- a/public/language/en-x-pirate/error.json +++ b/public/language/en-x-pirate/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Invalid Data", + "invalid-json": "Invalid JSON", "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Invalid title!", "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Please specify both a username and password", "invalid-search-term": "Invalid search term", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/en-x-pirate/global.json b/public/language/en-x-pirate/global.json index c5dcf3a819..b3856a69b1 100644 --- a/public/language/en-x-pirate/global.json +++ b/public/language/en-x-pirate/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/en-x-pirate/topic.json b/public/language/en-x-pirate/topic.json index 519cc5cd74..9c41963d50 100644 --- a/public/language/en-x-pirate/topic.json +++ b/public/language/en-x-pirate/topic.json @@ -14,6 +14,7 @@ "quote": "Quote", "reply": "Reply", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Unlock Topic", "thread_tools.move": "Move Topic", "thread_tools.move_all": "Move All", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Delete Topic", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Are you sure you want to restore this post?", "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Loading Categories", - "disabled_categories_note": "Disabled Categories are greyed out", "confirm_move": "Move", "confirm_fork": "Fork", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Move Post", "post_moved": "Post moved!", "fork_topic": "Fork Topic", - "topic_will_be_moved_to": "This topic will be moved to the category", "fork_topic_instruction": "Click the posts you want to fork", "fork_no_pids": "No posts selected!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/es/admin/advanced/database.json b/public/language/es/admin/advanced/database.json index 9c51814073..16c72eb046 100644 --- a/public/language/es/admin/advanced/database.json +++ b/public/language/es/admin/advanced/database.json @@ -2,35 +2,35 @@ "x-b": "%1 b", "x-mb": "%1 mb", "x-gb": "%1 gb", - "uptime-seconds": "Uptime in Seconds", - "uptime-days": "Uptime in Days", + "uptime-seconds": "Tiempo de acitividad en Segundos", + "uptime-days": "Tiempo de actividad en días", "mongo": "Mongo", - "mongo.version": "MongoDB Version", - "mongo.storage-engine": "Storage Engine", + "mongo.version": "Versión MongoDB", + "mongo.storage-engine": "Motor de almacenamiento", "mongo.collections": "Colecciones", "mongo.objects": "Objetos", "mongo.avg-object-size": "Tamaño promedio por Objeto", "mongo.data-size": "Tamaño de los Datos", - "mongo.storage-size": "Storage Size", - "mongo.index-size": "Index Size", - "mongo.file-size": "File Size", - "mongo.resident-memory": "Resident Memory", + "mongo.storage-size": "Tamaño del almacenamiento", + "mongo.index-size": "Tamaño del Índice", + "mongo.file-size": "Tamaño del fichero", + "mongo.resident-memory": "Memoria Residente", "mongo.virtual-memory": "Memoria Virtual", - "mongo.mapped-memory": "Mapped Memory", - "mongo.raw-info": "MongoDB Raw Info", + "mongo.mapped-memory": "Memoria Mapeada", + "mongo.raw-info": "Fila de Información MongoDB", "redis": "Redis", - "redis.version": "Redis Version", - "redis.connected-clients": "Connected Clients", + "redis.version": "Versión de Redis", + "redis.connected-clients": "Clientes Conectados", "redis.connected-slaves": "Esclavos Conectados", "redis.blocked-clients": "Clientes Bloqueados", "redis.used-memory": "Memoria Utilizada", - "redis.memory-frag-ratio": "Memory Fragmentation Ratio", + "redis.memory-frag-ratio": "Proporción de Fragmentación de la Memoria", "redis.total-connections-recieved": "Total de Conexiones Recividas ", "redis.total-commands-processed": "Total de Comandos Procesados", "redis.iops": "Operaciones Instantáneas por Segundo", - "redis.keyspace-hits": "Keyspace Hits", - "redis.keyspace-misses": "Keyspace Misses", - "redis.raw-info": "Redis Raw Info" + "redis.keyspace-hits": "Pulsaciones de espaciado del teclado", + "redis.keyspace-misses": "Fallos de espaciado del teclado", + "redis.raw-info": "Fila de Información de Redis" } \ No newline at end of file diff --git a/public/language/es/admin/appearance/customise.json b/public/language/es/admin/appearance/customise.json index 9578bf423d..ad8ae104a8 100644 --- a/public/language/es/admin/appearance/customise.json +++ b/public/language/es/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Cabezera personalizada", "custom-header.description": "Introduce HTML personalizado aquí (ej. JavaScript, Meta Etiquetas, etc.), el cual se adjuntará a la sección <head> del código de su foro.", - "custom-header.enable": "Activar cabecera personalizada" + "custom-header.enable": "Activar cabecera personalizada", + + "custom-css.livereload": "Activar Recargar en Vivo", + "custom-css.livereload.description": "Activar esto para forzar todas las sesiones en todos los dispositivos que recaen de tu cuenta a limpiar cada vez que tú haces clic en guardar" } \ No newline at end of file diff --git a/public/language/es/admin/development/info.json b/public/language/es/admin/development/info.json index a068347c43..912232a52d 100644 --- a/public/language/es/admin/development/info.json +++ b/public/language/es/admin/development/info.json @@ -1,16 +1,18 @@ { - "you-are-on": "Info - Tu estas en %1:%2", + "you-are-on": "Info - Tú estás en %1:%2", + "nodes-responded": "¡%1 nodos respondieron en %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "en-linea", "git": "git", + "memory": "memoria", "load": "cargar", - "uptime": "uptime", + "uptime": "tiempo de actividad", - "registered": "Registered", - "sockets": "Sockets", - "guests": "Guests", + "registered": "Registrado", + "sockets": "Toma", + "guests": "Invitados", - "info": "Info" + "info": "Información" } \ No newline at end of file diff --git a/public/language/es/admin/development/logger.json b/public/language/es/admin/development/logger.json index 6ab9558149..018f676d48 100644 --- a/public/language/es/admin/development/logger.json +++ b/public/language/es/admin/development/logger.json @@ -1,12 +1,12 @@ { - "logger-settings": "Logger Settings", + "logger-settings": "Ajustes de registro", "description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.", - "explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.", - "enable-http": "Enable HTTP logging", - "enable-socket": "Enable socket.io event logging", - "file-path": "Path to log file", + "explanation": "Simplemente marca/desmarca los ajustes de registro para activar o desactivar registro en el aire. No se necesita reinicio.", + "enable-http": "Activar registro HTTP", + "enable-socket": "Activar el evento de registro socket.io ", + "file-path": "Ruta al fichero log", "file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal", - "control-panel": "Logger Control Panel", - "update-settings": "Update Logger Settings" + "control-panel": "Panel de Control de Registro", + "update-settings": "Actualizar Ajustes de Registro" } \ No newline at end of file diff --git a/public/language/es/admin/general/languages.json b/public/language/es/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/es/admin/general/languages.json +++ b/public/language/es/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/es/admin/manage/categories.json b/public/language/es/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/es/admin/manage/categories.json +++ b/public/language/es/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/es/admin/settings/general.json b/public/language/es/admin/settings/general.json index 3f2814bd88..42256a2356 100644 --- a/public/language/es/admin/settings/general.json +++ b/public/language/es/admin/settings/general.json @@ -1,11 +1,11 @@ { - "site-settings": "Site Settings", - "title": "Site Title", - "title.name": "Your Community Name", - "title.show-in-header": "Show Site Title in Header", - "browser-title": "Browser Title", - "browser-title-help": "If no browser title is specified, the site title will be used", - "title-layout": "Title Layout", + "site-settings": "Ajustes del Sitio", + "title": "Título del Sitio", + "title.name": "Nombre de tu Comunidad", + "title.show-in-header": "Mostrar Título del Sitio en el Encabezado", + "browser-title": "Título del Navegador", + "browser-title-help": "Si no se especifica el título del navegador, se utilizará el título del sitio", + "title-layout": "Plantilla del Sitio", "title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}", "description.placeholder": "A short description about your community", "description": "Site Description", diff --git a/public/language/es/admin/settings/user.json b/public/language/es/admin/settings/user.json index 395ae98b58..b3fde20150 100644 --- a/public/language/es/admin/settings/user.json +++ b/public/language/es/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/es/email.json b/public/language/es/email.json index ed986ea853..7eed5d4d2d 100644 --- a/public/language/es/email.json +++ b/public/language/es/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "La notificación de este mensaje se te ha enviado debido a tus ajustes de subscripción.", "test.text1": "Este es un email de prueba para verificar que el envío de email está ajustado correctamente para tu NodeBB", "unsub.cta": "Haz click aquí para modificar los ajustes.", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "Has sido baneado de %1", + "banned.text1": "El usuario %1 ha sido baneado de %2.", + "banned.text2": "Este ban dura hasta %1.", + "banned.text3": "La razón por la que has sido baneado: ", "closing": "¡Gracias!" } \ No newline at end of file diff --git a/public/language/es/error.json b/public/language/es/error.json index f4d5c8fdef..48c8a87c0b 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Datos no válidos", + "invalid-json": "JSON no válido", "not-logged-in": "No has iniciado sesión.", "account-locked": "Tu cuenta ha sido bloqueada temporalmente.", "search-requires-login": "¡Buscar requiere estar registrado! Por favor, entra o regístrate.", @@ -12,6 +13,7 @@ "invalid-title": "¡Título no válido!", "invalid-user-data": "Datos de usuario no válidos", "invalid-password": "Contraseña no válida", + "invalid-login-credentials": "Datos de acceso no válidos", "invalid-username-or-password": "Por favor especifica tanto un usuario como contraseña", "invalid-search-term": "Término de búsqueda inválido", "csrf-invalid": "El acceso ha fallado porque tu sesión ha expirado. Por favor prueba otra vez.", @@ -30,7 +32,7 @@ "password-too-long": "Contraseña muy corta", "user-banned": "Usuario baneado", "user-banned-reason": "Lo siento, esta cuenta ha sido baneada ( Razon: %1 )", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "Lo siento, esta cuenta ha sido baneada hasta %1 ( Razon: %2 )", "user-too-new": "Lo sentimos, es necesario que esperes %1 segundo(s) antes poder hacer tu primera publicación", "blacklisted-ip": "Lo sentimos, tu dirección IP ha sido baneada de esta comunidad. Si crees que debe de haber un error, por favor contacte con un administrador.", "ban-expiry-missing": "Por favor pon una fecha de fin del ban", @@ -105,7 +107,7 @@ "chat-disabled": "El sistema de chat está deshabilitado", "too-many-messages": "Has enviado demasiados mensajes, por favor espera un poco.", "invalid-chat-message": "Mensaje de Chat inválido", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "Los mensajes de chat no pueden ser mas largo de %1 caracteres.", "cant-edit-chat-message": "No tienes permiso para editar este mensaje", "cant-remove-last-user": "No puedes eliminar el último usuario", "cant-delete-chat-message": "No tienes permiso para eliminar este mensaje", diff --git a/public/language/es/flags.json b/public/language/es/flags.json index 012d3181b0..24e723a0f4 100644 --- a/public/language/es/flags.json +++ b/public/language/es/flags.json @@ -17,7 +17,7 @@ "filter-targetUid": "Indicador UID", "filter-type": "Tipo de indicador", "filter-type-all": "Todo el contenido", - "filter-type-post": "Post", + "filter-type-post": "Mensaje", "filter-state": "estado", "filter-assignee": "UID asignado", "filter-cid": "Categoria", @@ -56,5 +56,5 @@ "modal-reason-offensive": "Ofensivo", "modal-reason-custom": "Razón para reportar este contenido...", "modal-submit": "Enviar reporte", - "modal-submit-success": "Content has been flagged for moderation." + "modal-submit-success": "El contenido se ha reportado para moderación." } \ No newline at end of file diff --git a/public/language/es/global.json b/public/language/es/global.json index 37608b7c42..14a72a5039 100644 --- a/public/language/es/global.json +++ b/public/language/es/global.json @@ -103,5 +103,7 @@ "cookies.message": "Esta web usa cookies para asegurar que usted recibe la mejor experiencia de navegación.", "cookies.accept": "De Acuerdo!", "cookies.learn_more": "Quiero saber más", - "edited": "Editado" + "edited": "Editado", + "disabled": "Desahabilitado", + "select": "Seleccionar" } \ No newline at end of file diff --git a/public/language/es/groups.json b/public/language/es/groups.json index 8179d021bb..b6e987ad0d 100644 --- a/public/language/es/groups.json +++ b/public/language/es/groups.json @@ -27,7 +27,7 @@ "details.disableJoinRequests": "Desactivar las peticiones de unión", "details.grant": "Conceder/Rescindir Propiedad", "details.kick": "Expulsar", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "¿ Estás seguro de que quieres eliminar a este miembro del grupo ?", "details.owner_options": "Administración De Grupo", "details.group_name": "Nombre de Grupo", "details.member_count": "Numero de Miembros", @@ -54,5 +54,5 @@ "upload-group-cover": "Cargar foto para el grupo", "bulk-invite-instructions": "Escribe una lista de nombres de usuario separados por comas a invitar a este grupo", "bulk-invite": "Invitación multiple", - "remove_group_cover_confirm": "Are you sure you want to remove the cover picture?" + "remove_group_cover_confirm": "¿ Estás seguro de que quieres eliminar la imagen de portada?" } \ No newline at end of file diff --git a/public/language/es/modules.json b/public/language/es/modules.json index ce157b6c08..3658673ca1 100644 --- a/public/language/es/modules.json +++ b/public/language/es/modules.json @@ -13,14 +13,14 @@ "chat.contacts": "Contactos", "chat.message-history": "Historial de mensajes", "chat.pop-out": "Mostrar en ventana independiente", - "chat.minimize": "Minimize", + "chat.minimize": "Minimizar", "chat.maximize": "Maximizar", "chat.seven_days": "7 días", "chat.thirty_days": "30 días", "chat.three_months": "3 meses", "chat.delete_message_confirm": "¿Estás seguro de que deseas eliminar este mensaje?", "chat.add-users-to-room": "Añadir usuarios a la sala", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Este usuario está en modo No molestar. ¿ Estás seguro de que quieres chatear con él ?", "composer.compose": "Crear", "composer.show_preview": "Ver Previsualización", "composer.hide_preview": "Ocultar Previsualización", diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index d06ddc9069..9dc633bc9d 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -10,15 +10,15 @@ "return_to": "Regresar a %1", "new_notification": "Nueva notificación", "you_have_unread_notifications": "Tienes notificaciones sin leer.", - "all": "All", - "topics": "Topics", - "replies": "Replies", + "all": "Todo", + "topics": "Temas", + "replies": "Respuestas", "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "follows": "Seguidores", + "upvote": "Votos positivos", + "new-flags": "Nuevos reportes", + "my-flags": "Reportado asignado a mí", + "bans": "Baneos", "new_message_from": "Nuevo mensaje de %1", "upvoted_your_post_in": "%1 ha votado positivamente tu respuesta en %2.", "upvoted_your_post_in_dual": "%1 y %2 han votado positivamente tu respuesta en %3.", @@ -28,9 +28,9 @@ "user_flagged_post_in": "%1 ha reportado una respuesta en %2", "user_flagged_post_in_dual": "%1 y %2 han reportado un post en %3", "user_flagged_post_in_multiple": "%1 y otras %2 personas han reportado un post en %3", - "user_flagged_user": "%1 flagged a user profile (%2)", - "user_flagged_user_dual": "%1 and %2 flagged a user profile (%3)", - "user_flagged_user_multiple": "%1 and %2 others flagged a user profile (%3)", + "user_flagged_user": "%1 reportó el perfil (%2) ", + "user_flagged_user_dual": "%1 y %2 reportaron el perfil (%3)", + "user_flagged_user_multiple": "%1 y otros %2 reportaron el perfil (%3) ", "user_posted_to": "%1 ha respondido a: %2", "user_posted_to_dual": "%1 y %2 han respondido a %3", "user_posted_to_multiple": "%1 y otras %2 personas han respondido a: %3", @@ -40,7 +40,7 @@ "user_started_following_you_multiple": "%1 y otras %2 personas comenzaron a seguirte.", "new_register": "%1 envió una solicitud de registro.", "new_register_multiple": "Hay %1 peticiones de registros pendientes de revisión", - "flag_assigned_to_you": "Flag %1 has been assigned to you", + "flag_assigned_to_you": "Reporte %1 te ha sido asignado.", "email-confirmed": "Correo electrónico confirmado", "email-confirmed-message": "Gracias por validar tu correo electrónico. Tu cuenta ya está completamente activa.", "email-confirm-error-message": "Hubo un problema al validar tu cuenta de correo electrónico. Quizá el código era erróneo o expiró...", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index 59b1e2624d..dd59a3e867 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -6,7 +6,7 @@ "popular-month": "Temas populares del mes", "popular-alltime": "Temas populares de siempre", "recent": "Temas recientes", - "flagged-content": "Flagged Content", + "flagged-content": "Contenido reportado", "ip-blacklist": "Lista negra de IPS", "users/online": "Conectados", "users/latest": "Últimos usuarios", @@ -27,8 +27,8 @@ "group": "Grupo de %1", "chats": "Chats", "chat": "Chatear con %1", - "flags": "Flags", - "flag-details": "Flag %1 Details", + "flags": "Reportes", + "flag-details": "Detalle de reporte %1", "account/edit": "Editar \"%1\"", "account/edit/password": "Editar contraseña de \"%1\"", "account/edit/username": "Editar nombre de usuario de \"%1\"", diff --git a/public/language/es/search.json b/public/language/es/search.json index 0d2d0a26b9..594b020cb5 100644 --- a/public/language/es/search.json +++ b/public/language/es/search.json @@ -12,7 +12,7 @@ "reply-count": "Número de Respuestas", "at-least": "De mínimo", "at-most": "De máximo", - "relevance": "Relevance", + "relevance": "Relevancia", "post-time": "Fecha de publicación", "newer-than": "Más reciente que", "older-than": "Más antiguo que", diff --git a/public/language/es/topic.json b/public/language/es/topic.json index a6cc026d23..50496a6106 100644 --- a/public/language/es/topic.json +++ b/public/language/es/topic.json @@ -13,8 +13,9 @@ "notify_me": "Serás notificado cuando haya nuevas respuestas en este tema", "quote": "Citar", "reply": "Responder", - "replies_to_this_post": "%1 Replies", - "last_reply_time": "Last reply", + "replies_to_this_post": "%1 Respuestas", + "one_reply_to_this_post": "1 Respuesta", + "last_reply_time": "Última respuesta", "reply-as-topic": "Responder como tema", "guest-login-reply": "Accede para responder", "edit": "Editar", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Reabrir tema", "thread_tools.move": "Mover tema", "thread_tools.move_all": "Mover todo", + "thread_tools.select_category": "Seleccionar categoría", "thread_tools.fork": "Dividir tema", "thread_tools.delete": "Borrar tema", "thread_tools.delete-posts": "Eliminar publicaciones", @@ -71,7 +73,6 @@ "post_restore_confirm": "¿Estás seguro de que quieres restaurar esta respuesta?", "post_purge_confirm": "¡Estás seguro de que quieres purgar esta publicación?", "load_categories": "Cargando categorías", - "disabled_categories_note": "Las categorías deshabilitadas están en gris", "confirm_move": "Mover", "confirm_fork": "Dividir", "bookmark": "Marcador", @@ -83,7 +84,6 @@ "move_post": "Mover mensaje", "post_moved": "¡Publicación movida correctamente!", "fork_topic": "Dividir tema", - "topic_will_be_moved_to": "Este tema será movido a la categoría", "fork_topic_instruction": "Pulsa en los mensajes que quieres dividir", "fork_no_pids": "¡No has seleccionado ningún mensaje!", "fork_pid_count": "%1 mensaje(s) seleccionados", diff --git a/public/language/es/user.json b/public/language/es/user.json index cb3779855d..79a81acb3e 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -33,7 +33,7 @@ "chat": "Chat", "chat_with": "Continuar chat con %1", "new_chat_with": "Empezar chat con %1", - "flag-profile": "Flag Profile", + "flag-profile": "Perfil de reporte", "follow": "Seguir", "unfollow": "Dejar de seguir", "more": "Más", @@ -60,14 +60,14 @@ "username_taken_workaround": "El nombre de usuario que has solicitada ya está siendo usado, por tanto lo hemos alterado ligeramente. Ahora eres conocido como %1.", "password_same_as_username": "Tu Constraseña es igual al nombre de Usuario, por favor seleccione otra Constraseña.", "password_same_as_email": "Tu contraseña es igual que tu dirección de correo, por favor elige otra contraseña.", - "weak_password": "Weak password.", + "weak_password": "Clase débil", "upload_picture": "Subir foto", "upload_a_picture": "Subir una foto", "remove_uploaded_picture": "Borrar Imagen subida", "upload_cover_picture": "Subir imagen de portada", - "remove_cover_picture_confirm": "Are you sure you want to remove the cover picture?", - "crop_picture": "Crop picture", - "upload_cropped_picture": "Crop and upload", + "remove_cover_picture_confirm": "¿ Estás seguro de borrar la imágen de portada ?", + "crop_picture": "Recortar imágen", + "upload_cropped_picture": "Recortar y subir", "settings": "Opciones", "show_email": "Mostrar mi correo electrónico", "show_fullname": "Mostrar mi nombre completo", @@ -132,5 +132,5 @@ "info.email-history": "HIstórico de Email", "info.moderation-note": "Nota de Moderación", "info.moderation-note.success": "Nota de moderación guardada", - "info.moderation-note.add": "Add note" + "info.moderation-note.add": "Añadir nota" } \ No newline at end of file diff --git a/public/language/et/admin/appearance/customise.json b/public/language/et/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/et/admin/appearance/customise.json +++ b/public/language/et/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/et/admin/development/info.json b/public/language/et/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/et/admin/development/info.json +++ b/public/language/et/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/et/admin/general/languages.json b/public/language/et/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/et/admin/general/languages.json +++ b/public/language/et/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/et/admin/manage/categories.json b/public/language/et/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/et/admin/manage/categories.json +++ b/public/language/et/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/et/admin/settings/user.json b/public/language/et/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/et/admin/settings/user.json +++ b/public/language/et/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/et/error.json b/public/language/et/error.json index 79979ceb3a..fa1f188b62 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Vigased andmed", + "invalid-json": "Invalid JSON", "not-logged-in": "Sa ei ole sisse logitud", "account-locked": "Su kasutaja on ajutiselt lukustatud", "search-requires-login": "Otsing nõuab kasutajat - palun registreeruge või logige sisse.", @@ -12,6 +13,7 @@ "invalid-title": "Vigane pealkiri!", "invalid-user-data": "Vigased kasutaja andmed", "invalid-password": "Vigane parool", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Palun täpsusta kasutajanime ja parooli", "invalid-search-term": "Vigane otsingusõna", "csrf-invalid": "Me ei saanud Sind sisse logida, võimalik, et tänu aegunud sessioonile, palun proovi uuesti", diff --git a/public/language/et/global.json b/public/language/et/global.json index 9fa7a44815..553419cdee 100644 --- a/public/language/et/global.json +++ b/public/language/et/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/et/topic.json b/public/language/et/topic.json index 63d556f10d..ce17ec70e0 100644 --- a/public/language/et/topic.json +++ b/public/language/et/topic.json @@ -14,6 +14,7 @@ "quote": "Tsiteeri", "reply": "Vasta", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Vasta teemana", "guest-login-reply": "Logi sisse, et vastata", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Taasava teema", "thread_tools.move": "Liiguta teema", "thread_tools.move_all": "Liiguta kõik", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Fork Topic", "thread_tools.delete": "Kustuta teema", "thread_tools.delete-posts": "Kustuta Postitusi", @@ -71,7 +73,6 @@ "post_restore_confirm": "Oled kindel, et soovid taastada antud postituse?", "post_purge_confirm": "Oled kindel, et soovid täielikult selle teema kustutada?", "load_categories": "Laen kategooriaid", - "disabled_categories_note": "Kinnised kategooriad on hallid", "confirm_move": "Liiguta", "confirm_fork": "Fork", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Liiguta postitust", "post_moved": "Postitus liigutatud!", "fork_topic": "Fork Topic", - "topic_will_be_moved_to": "See teema liigutatakse antud kategooriasse", "fork_topic_instruction": "Vajuta postitustele, mida soovid forkida", "fork_no_pids": "Sa ei ole postitusi valinud!", "fork_pid_count": "%1 postitus(t) valitud", diff --git a/public/language/fa-IR/admin/appearance/customise.json b/public/language/fa-IR/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/fa-IR/admin/appearance/customise.json +++ b/public/language/fa-IR/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/fa-IR/admin/development/info.json b/public/language/fa-IR/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/fa-IR/admin/development/info.json +++ b/public/language/fa-IR/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/fa-IR/admin/general/languages.json b/public/language/fa-IR/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/fa-IR/admin/general/languages.json +++ b/public/language/fa-IR/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/fa-IR/admin/manage/categories.json b/public/language/fa-IR/admin/manage/categories.json index f9d9255e17..6b021ca095 100644 --- a/public/language/fa-IR/admin/manage/categories.json +++ b/public/language/fa-IR/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/fa-IR/admin/settings/user.json b/public/language/fa-IR/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/fa-IR/admin/settings/user.json +++ b/public/language/fa-IR/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/fa-IR/error.json b/public/language/fa-IR/error.json index 92eff6f85f..885cbc54e0 100644 --- a/public/language/fa-IR/error.json +++ b/public/language/fa-IR/error.json @@ -1,5 +1,6 @@ { "invalid-data": "داده(های) نامعتبر", + "invalid-json": "Invalid JSON", "not-logged-in": "وارد حساب کاربری نشده‌اید.", "account-locked": "حساب کاربری شما موقتاً مسدود شده است.", "search-requires-login": "استفاده از جستجو نیازمند ورود با نام‌کاربری و رمز‌عبور است. لطفا ابتدا وارد شوید.", @@ -12,6 +13,7 @@ "invalid-title": "عنوان نامعتبر است!", "invalid-user-data": "داده‌های کاربر نامعتبر است.", "invalid-password": "کلمه عبور نامعتبر است.", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "لطفا هم نام کاربری و هم کلمه عبور را مشخص کنید", "invalid-search-term": "کلمه جستجو نامعتبر است", "csrf-invalid": "اجازه ورود شما تمام شده است، لطفا دوباره وارد شوید.", diff --git a/public/language/fa-IR/global.json b/public/language/fa-IR/global.json index a1c4034065..6494a06de6 100644 --- a/public/language/fa-IR/global.json +++ b/public/language/fa-IR/global.json @@ -103,5 +103,7 @@ "cookies.message": "این وب‌سایت از کوکی شما برای اطمینان و تجربه استفاده بهتر از وب‌سایت ما استفاده می‌کند.", "cookies.accept": "فهمیدم!", "cookies.learn_more": "بیشتر بدانید", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/fa-IR/topic.json b/public/language/fa-IR/topic.json index 48adb9b76c..647c7bb79a 100644 --- a/public/language/fa-IR/topic.json +++ b/public/language/fa-IR/topic.json @@ -14,6 +14,7 @@ "quote": "نقل قول", "reply": "پاسخ", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "پاسخ به موضوع", "guest-login-reply": "وارد شوید تا پست بفرستید", @@ -58,6 +59,7 @@ "thread_tools.unlock": "باز کردن موضوع", "thread_tools.move": "جابجا کردن موضوع", "thread_tools.move_all": "جابجایی همه", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "شاخه ساختن از موضوع", "thread_tools.delete": "پاک کردن موضوع", "thread_tools.delete-posts": "حذف پست ها", @@ -71,7 +73,6 @@ "post_restore_confirm": "آیا از بازگردانی این پست اطمینان دارید؟", "post_purge_confirm": "آیا از پاک کردن این پست اطمینان دارید؟", "load_categories": "بارگذاری دسته‌ها", - "disabled_categories_note": "دسته‌های از کار افتاده به رنگ خاکستری در می‌آیند", "confirm_move": "جابه‌جا کردن", "confirm_fork": "شاخه ساختن", "bookmark": "نشانک", @@ -83,7 +84,6 @@ "move_post": "جابه‌جایی موضوع", "post_moved": "پست جابه‌جا شد!", "fork_topic": "شاخه ساختن از موضوع", - "topic_will_be_moved_to": "این موضوع جابه‌جا خواهد شد به دستهٔ", "fork_topic_instruction": "پست‌هایی را که می‌خواهید به موضوع تازه ببرید، انتخاب کنید", "fork_no_pids": "هیچ پستی انتخاب نشده!", "fork_pid_count": "%1 پست (ها) انتخاب شده اند", diff --git a/public/language/fa-IR/user.json b/public/language/fa-IR/user.json index b2d3fc29a4..99526d6ed0 100644 --- a/public/language/fa-IR/user.json +++ b/public/language/fa-IR/user.json @@ -114,8 +114,8 @@ "select-skin": "انتخاب یک پوسته", "select-homepage": "انتخاب صفحه اصلی", "homepage": "صفحه اصلی", - "homepage_description": "Select a page to use as the forum homepage or 'None' to use the default homepage.", - "custom_route": "Custom Homepage Route", + "homepage_description": "یک صفحه را به عنوان خانه انتخاب کنید یا با انتخاب \"هیچکدام\" صفحه‌ی پیش فرض برای شما انتخاب می‌شود. ", + "custom_route": "مسیر صفحه‌ی اختصاصی", "custom_route_help": "Enter a route name here, without any preceding slash (e.g. \"recent\", or \"popular\")", "sso.title": "Single Sign-on Services", "sso.associated": "Associated with", @@ -132,5 +132,5 @@ "info.email-history": "تاریخچه رایانامه", "info.moderation-note": "یادداشت مدیر", "info.moderation-note.success": "یادداشت مدیر ذخیره شد", - "info.moderation-note.add": "Add note" + "info.moderation-note.add": "افزودن یادداشت" } \ No newline at end of file diff --git a/public/language/fi/admin/appearance/customise.json b/public/language/fi/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/fi/admin/appearance/customise.json +++ b/public/language/fi/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/fi/admin/development/info.json b/public/language/fi/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/fi/admin/development/info.json +++ b/public/language/fi/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/fi/admin/general/languages.json b/public/language/fi/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/fi/admin/general/languages.json +++ b/public/language/fi/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/fi/admin/manage/categories.json b/public/language/fi/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/fi/admin/manage/categories.json +++ b/public/language/fi/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/fi/admin/settings/user.json b/public/language/fi/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/fi/admin/settings/user.json +++ b/public/language/fi/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/fi/error.json b/public/language/fi/error.json index 75e5d3609b..fc904202b2 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Virheellinen data", + "invalid-json": "Invalid JSON", "not-logged-in": "Et taida olla kirjautuneena sisään.", "account-locked": "Käyttäjätilisi on lukittu väliaikaisesti", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Virheellinen otsikko!", "invalid-user-data": "Virheellinen käyttäjätieto", "invalid-password": "Virheellinen salasana", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Ole hyvä ja anna sekä käyttäjänimi että salasana", "invalid-search-term": "Virheellinen hakutermi", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/fi/global.json b/public/language/fi/global.json index 690bde168b..721a489bfa 100644 --- a/public/language/fi/global.json +++ b/public/language/fi/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/fi/topic.json b/public/language/fi/topic.json index 56f51da9bf..84b298c455 100644 --- a/public/language/fi/topic.json +++ b/public/language/fi/topic.json @@ -14,6 +14,7 @@ "quote": "Lainaa", "reply": "Vastaa", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Kirjaudu sisään voidaksesi vastata", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Poista aiheen lukitus", "thread_tools.move": "Siirrä aihe", "thread_tools.move_all": "Siirrä kaikki", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Haaroita aihe", "thread_tools.delete": "Poista aihe", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Haluatko varmasti palauttaa tämän viestin?", "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Ladataan aihealueita", - "disabled_categories_note": "Käytöstä poistetut aihealueet ovat harmaina", "confirm_move": "Siirrä", "confirm_fork": "Haaroita", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Siirrä viesti", "post_moved": "Post moved!", "fork_topic": "Haaroita keskustelu", - "topic_will_be_moved_to": "Tämä keskustelu siirretään aihealueelle", "fork_topic_instruction": "Napsauta viestejä, jotka haluat haaroittaa", "fork_no_pids": "Ei valittuja viestejä!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/fr/admin/appearance/customise.json b/public/language/fr/admin/appearance/customise.json index f06d1c65f3..e867039801 100644 --- a/public/language/fr/admin/appearance/customise.json +++ b/public/language/fr/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "En-tête personnalisé", "custom-header.description": "Entrez votre code HTML ici (ex. Javascripts, Méta tags, etc…), qui seront ajoutés à la section <head> du code de votre forum.", - "custom-header.enable": "Activer les en-têtes personnalisés" + "custom-header.enable": "Activer les en-têtes personnalisés", + + "custom-css.livereload": "Activer le rechargement en direct", + "custom-css.livereload.description": "Activez cette option pour forcer toutes les sessions sur chaque appareil connecté à votre compte à se rafraichir lorsque vous cliquez sur Enregistrer." } \ No newline at end of file diff --git a/public/language/fr/admin/appearance/themes.json b/public/language/fr/admin/appearance/themes.json index bc9ca500e4..975c3d99ba 100644 --- a/public/language/fr/admin/appearance/themes.json +++ b/public/language/fr/admin/appearance/themes.json @@ -1,7 +1,7 @@ { "checking-for-installed": "Vérification des thèmes installés…", "homepage": "Page d'accueil", - "select-theme": "Choisir un thème", + "select-theme": "Sélectionner ce thème", "current-theme": "Thème actuel", "no-themes": "Aucun thème installé", "revert-confirm": "Êtes-vous sûr de vouloir restaurer le thème NodeBB par défaut ?", diff --git a/public/language/fr/admin/development/info.json b/public/language/fr/admin/development/info.json index d367863480..ce9f8f43ee 100644 --- a/public/language/fr/admin/development/info.json +++ b/public/language/fr/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - Vous êtes sur %1:%2", + "nodes-responded": "%1 noeuds ont répondu en %2ms !", "host": "hôte", "pid": "pid", "nodejs": "nodejs", "online": "en ligne", "git": "git", + "memory": "mémoire", "load": "charge", "uptime": "disponibilité", diff --git a/public/language/fr/admin/general/languages.json b/public/language/fr/admin/general/languages.json index 0dfa80733c..51ee9f7f01 100644 --- a/public/language/fr/admin/general/languages.json +++ b/public/language/fr/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Réglages linguistiques", "description": "La langue par défaut détermine les réglages pour tous les utilisateurs qui visitent votre forum.
Les utilisateurs peuvent ensuite modifier la langue par défaut sur leur page de réglages.", - "default-language": "Langue par défaut" + "default-language": "Langue par défaut", + "auto-detect": "Détection automatique de la langue pour les invités" } \ No newline at end of file diff --git a/public/language/fr/admin/manage/categories.json b/public/language/fr/admin/manage/categories.json index ab807c22af..0d6ace6124 100644 --- a/public/language/fr/admin/manage/categories.json +++ b/public/language/fr/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Class personnalisée", "num-recent-replies": "# de réponses récentes", "ext-link": "Lien externe", + "is-section": "Traiter cette catégorie comme une section", "upload-image": "Envoyer une image", "delete-image": "Enlever", "category-image": "Image de la catégorie", diff --git a/public/language/fr/admin/settings/user.json b/public/language/fr/admin/settings/user.json index b99fc08439..0f8bc4e171 100644 --- a/public/language/fr/admin/settings/user.json +++ b/public/language/fr/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Nombre maximum d'invitations par utilisateur", "max-invites": "Nombre maximum d'invitations par utilisateur", "max-invites-help": "0 pour supprimer cette restriction. Les admins n'ont aucune restriction
Valable uniquement pour \"Uniquement sur invitation\"", + "invite-expiration": "Expiration des invitations", + "invite-expiration-help": "nombre de jours avant que l'invitation n'expire.", "min-username-length": "Longueur minimum du nom d'utilisateur", "max-username-length": "Longueur maxmum du nom d'utilisateur", "min-password-length": "Longueur minimum du mot de passe", diff --git a/public/language/fr/error.json b/public/language/fr/error.json index 9b8d880ae7..009cbfdc93 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Données invalides", + "invalid-json": "JSON invalide", "not-logged-in": "Vous ne semblez pas être connecté.", "account-locked": "Votre compte a été temporairement suspendu", "search-requires-login": "Rechercher nécessite d'avoir un compte. Veuillez vous identifier ou vous enregistrer.", @@ -12,6 +13,7 @@ "invalid-title": "Titre invalide !", "invalid-user-data": "Données utilisateur invalides", "invalid-password": "Mot de passe invalide", + "invalid-login-credentials": "Certificat d'identification invalide", "invalid-username-or-password": "Veuillez entrer un nom d'utilisateur et un mot de passe", "invalid-search-term": "Données de recherche invalides", "csrf-invalid": "Nous ne pouvons pas vous connectez, possiblement car votre session a expiré. Merci de réessayer.", diff --git a/public/language/fr/global.json b/public/language/fr/global.json index 51a1ba5f49..299221c150 100644 --- a/public/language/fr/global.json +++ b/public/language/fr/global.json @@ -103,5 +103,7 @@ "cookies.message": "Ce site utilise des cookies pour vous permettre d'avoir la meilleure expérience possible.", "cookies.accept": "Compris !", "cookies.learn_more": "En savoir plus", - "edited": "Modifié" + "edited": "Modifié", + "disabled": "Désactivé", + "select": "Sélectionner" } \ No newline at end of file diff --git a/public/language/fr/topic.json b/public/language/fr/topic.json index 0c21952b4f..1c59ccba72 100644 --- a/public/language/fr/topic.json +++ b/public/language/fr/topic.json @@ -14,6 +14,7 @@ "quote": "Citer", "reply": "Répondre", "replies_to_this_post": "%1 réponses", + "one_reply_to_this_post": "1 réponse", "last_reply_time": "Dernière réponse", "reply-as-topic": "Répondre à l'aide d'un sujet", "guest-login-reply": "Se connecter pour répondre", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Déverouiller le sujet", "thread_tools.move": "Déplacer le sujet", "thread_tools.move_all": "Déplacer tout", + "thread_tools.select_category": "Sélectionner une catégorie", "thread_tools.fork": "Scinder le sujet", "thread_tools.delete": "Supprimer le sujet", "thread_tools.delete-posts": "Supprimer les messages", @@ -71,7 +73,6 @@ "post_restore_confirm": "Êtes-vous sûr de bien vouloir restaurer ce message ?", "post_purge_confirm": "Êtes-vous sûr de bien vouloir supprimer définitivement ce sujet ?", "load_categories": "Chargement des catégories en cours", - "disabled_categories_note": "Les catégories désactivées sont grisées", "confirm_move": "Déplacer", "confirm_fork": "Scinder", "bookmark": "Marque-page", @@ -83,7 +84,6 @@ "move_post": "Déplacer", "post_moved": "Message déplacé !", "fork_topic": "Scinder le sujet", - "topic_will_be_moved_to": "Ce sujet sera déplacé vers la catégorie", "fork_topic_instruction": "Cliquez sur les postes à scinder", "fork_no_pids": "Aucun post sélectionné !", "fork_pid_count": "%1 message(s) sélectionné(s)", diff --git a/public/language/gl/admin/appearance/customise.json b/public/language/gl/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/gl/admin/appearance/customise.json +++ b/public/language/gl/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/gl/admin/development/info.json b/public/language/gl/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/gl/admin/development/info.json +++ b/public/language/gl/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/gl/admin/general/languages.json b/public/language/gl/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/gl/admin/general/languages.json +++ b/public/language/gl/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/gl/admin/manage/categories.json b/public/language/gl/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/gl/admin/manage/categories.json +++ b/public/language/gl/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/gl/admin/settings/user.json b/public/language/gl/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/gl/admin/settings/user.json +++ b/public/language/gl/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/gl/error.json b/public/language/gl/error.json index f6fd1bca9b..31afca5970 100644 --- a/public/language/gl/error.json +++ b/public/language/gl/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Datos non válidos", + "invalid-json": "Invalid JSON", "not-logged-in": "Parece que estás desconectado.", "account-locked": "A túa conta foi bloqueada temporalmente.", "search-requires-login": "As buscas requiren unha conta. Por favor inicia sesión ou rexístrate.", @@ -12,6 +13,7 @@ "invalid-title": "Título inválido!", "invalid-user-data": "Datos de Usuario Inválidos", "invalid-password": "Contrasinal Inválido", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Especifica ámbolos dous por favor, nome de usuario e contrasinal", "invalid-search-term": "Termo de búsqueda inválido", "csrf-invalid": "Non fomos capaces de entrar, probablemente porque a que a sesión expirou. Por favor, téntao de novo", diff --git a/public/language/gl/global.json b/public/language/gl/global.json index 58f0d18694..50a2e6015d 100644 --- a/public/language/gl/global.json +++ b/public/language/gl/global.json @@ -103,5 +103,7 @@ "cookies.message": "Esta web emprega cookies para asegurar que recibes unha mellor experiencia de navegación.", "cookies.accept": "De Acordo!", "cookies.learn_more": "Saber máis", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/gl/topic.json b/public/language/gl/topic.json index 5c5e19ed43..ace8074655 100644 --- a/public/language/gl/topic.json +++ b/public/language/gl/topic.json @@ -14,6 +14,7 @@ "quote": "Citar", "reply": "Responder", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Responder como tema", "guest-login-reply": "Identifícate para responder", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Reabrir Tema", "thread_tools.move": "Mover Tema", "thread_tools.move_all": "Mover todo", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Dividir Tema", "thread_tools.delete": "Borrar Tema", "thread_tools.delete-posts": "Eliminar publicacións", @@ -71,7 +73,6 @@ "post_restore_confirm": "Estás seguro de que desexas restaurar esta publicación?", "post_purge_confirm": "Estás seguro de que desexas purgar esta publicación??", "load_categories": "Cargando categorías", - "disabled_categories_note": "As categorías deshabilitadas están en gris", "confirm_move": "Mover", "confirm_fork": "Dividir", "bookmark": "Marcador", @@ -83,7 +84,6 @@ "move_post": "Mover publicación", "post_moved": "Publicación movida correctamente!", "fork_topic": "Dividir Tema", - "topic_will_be_moved_to": "Este tema será movido á categoría", "fork_topic_instruction": "Fai clic nas publicacións que queiras dividir", "fork_no_pids": "Non seleccionaches ninguna publicación!", "fork_pid_count": "%1 mensaxe(s) seleccionada(s)", diff --git a/public/language/he/admin/admin.json b/public/language/he/admin/admin.json index fe4b302c37..073a6b28be 100644 --- a/public/language/he/admin/admin.json +++ b/public/language/he/admin/admin.json @@ -1,7 +1,7 @@ { - "alert.confirm-reload": "Are you sure you wish to reload NodeBB?", - "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", + "alert.confirm-reload": "האם אתה בטוח שאתה רוצה לטעון מחדש את NodeBB?", + "alert.confirm-restart": "האם אתה בטוח שאתה רוצה לאתחל מחדש את NodeBB?", - "acp-title": "%1 | NodeBB Admin Control Panel", + "acp-title": "%1 | לוח בקרה לאדמין NodeBB", "settings-header-contents": "תוכן" } \ No newline at end of file diff --git a/public/language/he/admin/advanced/cache.json b/public/language/he/admin/advanced/cache.json index 89a09fe5d8..934943c4e9 100644 --- a/public/language/he/admin/advanced/cache.json +++ b/public/language/he/admin/advanced/cache.json @@ -1,11 +1,11 @@ { - "post-cache": "Post Cache", - "posts-in-cache": "Posts in Cache", - "average-post-size": "Average Post Size", - "length-to-max": "Length / Max", - "percent-full": "%1% Full", - "post-cache-size": "Post Cache Size", - "items-in-cache": "Items in Cache", + "post-cache": "מטמון פוסטים", + "posts-in-cache": "פוסטים במטמון", + "average-post-size": "גודל פוסט ממוצע", + "length-to-max": "אורך / מקסימום", + "percent-full": "%1% מלא", + "post-cache-size": "גודל מטמון פוסטים", + "items-in-cache": "פריטים במטמון", "control-panel": "לוח הבקרה", - "update-settings": "Update Cache Settings" + "update-settings": "עדכן הגדרות מטמון" } \ No newline at end of file diff --git a/public/language/he/admin/advanced/database.json b/public/language/he/admin/advanced/database.json index b88ca6fc82..8b16786e86 100644 --- a/public/language/he/admin/advanced/database.json +++ b/public/language/he/admin/advanced/database.json @@ -1,20 +1,20 @@ { - "x-b": "%1 b", - "x-mb": "%1 mb", - "x-gb": "%1 gb", - "uptime-seconds": "Uptime in Seconds", - "uptime-days": "Uptime in Days", + "x-b": "%1 בתים", + "x-mb": "%1 מגה בייט", + "x-gb": "%1 ג'יגה בייט", + "uptime-seconds": "זמן מאתחול אחרון בשניות", + "uptime-days": "זמן מאתחול אחרון בימים", "mongo": "Mongo", - "mongo.version": "MongoDB Version", - "mongo.storage-engine": "Storage Engine", - "mongo.collections": "Collections", - "mongo.objects": "Objects", - "mongo.avg-object-size": "Avg. Object Size", - "mongo.data-size": "Data Size", - "mongo.storage-size": "Storage Size", - "mongo.index-size": "Index Size", - "mongo.file-size": "File Size", + "mongo.version": "גרסאת MongoDB", + "mongo.storage-engine": "מנוע אחסון", + "mongo.collections": "אוסף", + "mongo.objects": "אובייקטים", + "mongo.avg-object-size": "גודל אובייקט ממוצע", + "mongo.data-size": "גודל המידע", + "mongo.storage-size": "גודל האחסון", + "mongo.index-size": "גודל האינדקס", + "mongo.file-size": "גודל הקובץ", "mongo.resident-memory": "Resident Memory", "mongo.virtual-memory": "Virtual Memory", "mongo.mapped-memory": "Mapped Memory", diff --git a/public/language/he/admin/appearance/customise.json b/public/language/he/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/he/admin/appearance/customise.json +++ b/public/language/he/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/he/admin/development/info.json b/public/language/he/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/he/admin/development/info.json +++ b/public/language/he/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/he/admin/general/languages.json b/public/language/he/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/he/admin/general/languages.json +++ b/public/language/he/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/he/admin/manage/categories.json b/public/language/he/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/he/admin/manage/categories.json +++ b/public/language/he/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/he/admin/settings/user.json b/public/language/he/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/he/admin/settings/user.json +++ b/public/language/he/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/he/category.json b/public/language/he/category.json index fbf21e942d..2d3fbfb4e1 100644 --- a/public/language/he/category.json +++ b/public/language/he/category.json @@ -10,11 +10,11 @@ "share_this_category": "שתף קטגוריה זו", "watch": "עקוב", "ignore": "התעלם", - "watching": "Watching", - "ignoring": "Ignoring", + "watching": "עוקב", + "ignoring": "מתעלם", "watching.description": "צפה בנושאים שלא נקראו", "ignoring.description": "אל תציג נושאים שאינם נקראו", - "watch.message": "You are now watching updates from this category and all subcategories", - "ignore.message": "You are now ignoring updates from this category and all subcategories", + "watch.message": "אתה כעת עוקב אחר עדכונים בקטגוריה זו וכל תת-הקטגוריות", + "ignore.message": "אתה כעת מתעלם מעדכונים בקטגוריה זו וכל תת-הקטגוריות", "watched-categories": "קטגוריות נעקבות" } \ No newline at end of file diff --git a/public/language/he/email.json b/public/language/he/email.json index aa2579e87a..1495b88c48 100644 --- a/public/language/he/email.json +++ b/public/language/he/email.json @@ -24,7 +24,7 @@ "digest.day": "יום", "digest.week": "שבוע", "digest.month": "חודש", - "digest.subject": "Digest for %1", + "digest.subject": "מקבץ עבור %1", "notif.chat.subject": "הודעת צ'אט חדשה התקבלה מ%1", "notif.chat.cta": "לחץ כאן כדי להמשיך את השיחה", "notif.chat.unsub.info": "התראה הצ'אט הזו נשלחה אליך על-פי הגדרות החשבון שלך.", @@ -32,9 +32,9 @@ "notif.post.unsub.info": "התראת הפוסט הזו נשלחה אליך על-פי הגדרות החשבון שלך.", "test.text1": "זהו אימייל ניסיון על מנת לוודא שהגדרות המייל בוצעו כהלכה בהגדרות NodeBB.", "unsub.cta": "לחץ כאן לשנות הגדרות אלו", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "הורחקת מ %1", + "banned.text1": "המשתמש %1 הורחק מ %2.", + "banned.text2": "הרחקה זו תמשך עד %1", + "banned.text3": "זו הסיבה שבגללה הורחקת:", "closing": "תודה!" } \ No newline at end of file diff --git a/public/language/he/error.json b/public/language/he/error.json index 16430c13d1..f5e33a6e10 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -1,5 +1,6 @@ { "invalid-data": "נתונים שגויים", + "invalid-json": "אובייקט JSON לא תקין", "not-logged-in": "נראה שאינך מחובר למערכת.", "account-locked": "חשבונך נחסם באופן זמני", "search-requires-login": "פעולת החיפוש דורשת חשבון - בבקשה התחבר או הרשם.", @@ -12,6 +13,7 @@ "invalid-title": "כותרת שגויה", "invalid-user-data": "מידע משתמש שגוי", "invalid-password": "סיסמא שגויה", + "invalid-login-credentials": "פרטי ההתחברות שגויים", "invalid-username-or-password": "אנא הגדר שם משתמש וסיסמה", "invalid-search-term": "מילת חיפוש לא תקינה", "csrf-invalid": "אין באפשרותנו לחבר אותך למערכת, מכיוון שעבר זמן רב מידי. אנא נסה שנית.", @@ -29,8 +31,8 @@ "username-too-long": "שם משתמש ארוך מדי", "password-too-long": "הסיסמה ארוכה מדי", "user-banned": "המשתמש מושעה", - "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason": "מצטערים, חשבון זה הורחק (סיבה: %1)", + "user-banned-reason-until": "מצטערים, חשבון זה הורחק עד %1 (סיבה: %2)", "user-too-new": "אנא המתן %1 שניות לפני פרסום ההודעה", "blacklisted-ip": "מצטערים, אך הורחקת מקהילה זו. אם הנך סבור שמדובר בטעות, אנא צור קשר עם מנהלי הקהילה.", "ban-expiry-missing": "אנא ספק תאריך סיום להרחקה זו.", @@ -46,7 +48,7 @@ "post-edit-duration-expired": "אתה רשאי לערוך פוסטים רק ל-%1 שניות לאחר הפרסום", "post-edit-duration-expired-minutes": "הנך רשאי לערוך תגובה עד %1 דקות מרגע פרסום התגובה.", "post-edit-duration-expired-minutes-seconds": "הנך רשאי לערוך תגובה עד %1 דקות %2 ושניות מרגע פרסום התגובה.", - "post-edit-duration-expired-hours": "You are only allowed to edit posts for %1 hour(s) after posting", + "post-edit-duration-expired-hours": "אתה מורשה לערוך פוסטים רק %1 שעות אחרי הפרסום.", "post-edit-duration-expired-hours-minutes": "You are only allowed to edit posts for %1 hour(s) %2 minute(s) after posting", "post-edit-duration-expired-days": "You are only allowed to edit posts for %1 day(s) after posting", "post-edit-duration-expired-days-hours": "You are only allowed to edit posts for %1 day(s) %2 hour(s) after posting", @@ -58,12 +60,12 @@ "post-delete-duration-expired-days": "You are only allowed to delete posts for %1 day(s) after posting", "post-delete-duration-expired-days-hours": "You are only allowed to delete posts for %1 day(s) %2 hour(s) after posting", "cant-delete-topic-has-reply": "אינך יכול למחוק נושא אחרי שכבר הגיבו בו.", - "cant-delete-topic-has-replies": "You can't delete your topic after it has %1 replies", + "cant-delete-topic-has-replies": "לא ניתן למחוק את הנושא לאחר שקיבל %1 תגובות", "content-too-short": "אנא הכנס פוסט ארוך יותר. פוסטים חייבים להכיל לפחות %1 תווים.", "content-too-long": "אנא הכנס פוסט קצר יותר. פוסטים חייבים להיות קצרים יותר מ-%1 תווים.", "title-too-short": "אנא הכנס כותרת ארוכה יותר. כותרות חייבות להכיל לפחות %1 תווים.", "title-too-long": "אנא הכנס כותרת קצרה יותר. כותרות אינן יכולות להיות ארוכות מ-%1 תווים.", - "category-not-selected": "Category not selected.", + "category-not-selected": "לא נבחרה קטגוריה", "too-many-posts": "אתה יכול לפרסם פוסט רק פעם ב-%1 שניות - אנא המתן לפני פרסום שוב", "too-many-posts-newbie": "כמשתמש חדש, אתה יכול לפרסם פוסט רק פעם ב-%1 שניות עד שיהיו לך %2 נקודות מוניטין - אנא המתן לפני פרסום שוב", "tag-too-short": "אנא הכנס תגית ארוכה יותר. תגיות חייבות להכיל לפחות %1 תווים", @@ -105,7 +107,7 @@ "chat-disabled": "מערכת הצ'אט לא פעילה", "too-many-messages": "שלחת יותר מדי הודעות, אנא המתן לזמן מה.", "invalid-chat-message": "הודעת צ'אט לא תקינה", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "הודעות צ'אט לא יכולות להיות ארוכות מ %1 תווים.", "cant-edit-chat-message": "אתה לא רשאי לערוך הודעה זו", "cant-remove-last-user": "אינך יכול למחוק את המשתמש האחרון", "cant-delete-chat-message": "אתה לא רשאי למחוק הודעה זו", @@ -127,6 +129,6 @@ "cant-kick-self": "אינך יכול להסיר את עצמך מהקבוצה", "no-users-selected": "לא נבחרו משתמשים", "invalid-home-page-route": "כתובת דף הבית הינה שגויה", - "invalid-session": "Session Mismatch", - "invalid-session-text": "It looks like your login session is no longer active, or no longer matches with the server. Please refresh this page." + "invalid-session": "סשן לא תקין", + "invalid-session-text": "נראה שסשן ההתחברות שלך כבר לא פעיל. אנא טען מחדש את העמוד." } \ No newline at end of file diff --git a/public/language/he/global.json b/public/language/he/global.json index 17d0b9023c..7b98b840e7 100644 --- a/public/language/he/global.json +++ b/public/language/he/global.json @@ -10,7 +10,7 @@ "500.title": "שגיאה פנימית.", "500.message": "אופס! נראה שמשהו השתבש!", "400.title": "בקשה שגויה", - "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.", + "400.message": "נראה שהלינק הזה לא תקין, בדוק ונסה שוב. אחרת, חזור לעמוד הבית.", "register": "הרשמה", "login": "התחברות", "please_log_in": "אנא התחבר", @@ -19,7 +19,7 @@ "welcome_back": "ברוכים השבים", "you_have_successfully_logged_in": "התחברת בהצלחה", "save_changes": "שמור שינויים", - "save": "Save", + "save": "שמור", "close": "סגור", "pagination": "עימוד", "pagination.out_of": "%1 מתוך %2", @@ -53,9 +53,9 @@ "topics": "נושאים", "posts": "פוסטים", "best": "הגבוה ביותר", - "upvoters": "Upvoters", + "upvoters": "מצביעי בעד", "upvoted": "הוצבע בעד", - "downvoters": "Downvoters", + "downvoters": "מצביעי נגד", "downvoted": "הוצבע נגד", "views": "צפיות", "reputation": "מוניטין", @@ -74,8 +74,8 @@ "norecentposts": "אין פוסטים מהזמן האחרון", "norecenttopics": "אין נושאים מהזמן החרון", "recentposts": "פוסטים אחרונים", - "recentips": "כתובות IP שנכנסו למערכת לאחרונה", - "moderator_tools": "Moderator Tools", + "recentips": "כתובות IP שהתחברו למערכת לאחרונה", + "moderator_tools": "כלי מוד", "away": "לא נמצא", "dnd": "נא לא להפריע", "invisible": "מוסתר", @@ -99,9 +99,11 @@ "allowed-file-types": "פורמטי הקבצים המורשים הם %1", "unsaved-changes": "יש לך שינויים שאינם נשמרו. האם הנך בטוח שברצונך להמשיך?", "reconnecting-message": "נראה שההתחברות שלך אל %1 אבדה, אנא המתן בזמן שהמערכת מנסה לחבר אותך מחדש", - "play": "Play", - "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", - "cookies.accept": "Got it!", - "cookies.learn_more": "Learn More", - "edited": "Edited" + "play": "נגן", + "cookies.message": "אתר זה משתמש ב cookies על מנת לשפר את חוויות המשתמש.", + "cookies.accept": "קיבלתי!", + "cookies.learn_more": "למד עוד", + "edited": "נערך", + "disabled": "לא מאופשר", + "select": "בחר" } \ No newline at end of file diff --git a/public/language/he/groups.json b/public/language/he/groups.json index 5080d02ce1..5be31f9d39 100644 --- a/public/language/he/groups.json +++ b/public/language/he/groups.json @@ -27,7 +27,7 @@ "details.disableJoinRequests": "בטל בקשות הצטרפות", "details.grant": "הענק/בטל בעלות", "details.kick": "גרש", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "האם אתה בטוח שאתה רוצה להסיר משתמש זה מהקבוצה?", "details.owner_options": "ניהול הקבוצה", "details.group_name": "שם הקבוצה", "details.member_count": "כמות משתמשים", @@ -52,7 +52,7 @@ "membership.reject": "דחה", "new-group.group_name": "שם קבוצה", "upload-group-cover": "העלה תמונת נושא לקבוצה", - "bulk-invite-instructions": "Enter a list of comma separated usernames to invite to this group", - "bulk-invite": "Bulk Invite", - "remove_group_cover_confirm": "Are you sure you want to remove the cover picture?" + "bulk-invite-instructions": "הזן רשימה מופרדת בפסיק של משתמשים שתרצה להזמין לקבוצה זו.", + "bulk-invite": "הזמן מספר משתמשים", + "remove_group_cover_confirm": "האם אתה בטוח שאתה רוצה להסיר את תמונת הקאבר?" } \ No newline at end of file diff --git a/public/language/he/login.json b/public/language/he/login.json index cbc60f999c..c0b5354129 100644 --- a/public/language/he/login.json +++ b/public/language/he/login.json @@ -5,8 +5,8 @@ "remember_me": "זכור אותי?", "forgot_password": "שכחת סיסמתך?", "alternative_logins": "התחבר באמצעות...", - "failed_login_attempt": "Login Unsuccessful", + "failed_login_attempt": "ההתחברות נכשלה", "login_successful": "התחברת בהצלחה!", "dont_have_account": "אין לך חשבון עדיין?", - "logged-out-due-to-inactivity": "You have been logged out of the Admin Control Panel due to inactivity" + "logged-out-due-to-inactivity": "התנתקת מפאנל האדמין בגלל חוסר אקטיביות" } \ No newline at end of file diff --git a/public/language/he/modules.json b/public/language/he/modules.json index 6e41dad53f..2e4e6a5554 100644 --- a/public/language/he/modules.json +++ b/public/language/he/modules.json @@ -6,21 +6,21 @@ "chat.user_typing": "%1 מקליד ...", "chat.user_has_messaged_you": "ל%1 יש הודעה עבורך.", "chat.see_all": "צפה בכל הצ'אטים", - "chat.mark_all_read": "Mark all chats read", + "chat.mark_all_read": "סמן את כל הצ'אטים כ'נקראו'", "chat.no-messages": "אנא בחר נמען על מנת לראות את היסטוריית הצ'אט איתו", "chat.no-users-in-room": "אין משתמשים בחדר הזה", "chat.recent-chats": "צ'אטים אחרונים", "chat.contacts": "אנשי קשר", "chat.message-history": "היסטוריית הודעות", "chat.pop-out": "הוצא את חלון הצ'אט", - "chat.minimize": "Minimize", + "chat.minimize": "צמצם", "chat.maximize": "הרחב", "chat.seven_days": "7 ימים", "chat.thirty_days": "30 ימים", "chat.three_months": "3 חודשים", "chat.delete_message_confirm": "האם אתה בטוח שברצונך למחוק הודעה זו?", "chat.add-users-to-room": "הוסף משתמשים לצ'אט", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "משתמש זה שינה את הסטטוס שלו ל 'לא להפריע'. אתה עדיין מעוניין לשוחח איתו?", "composer.compose": "צור", "composer.show_preview": "הצג תצוגה מקדימה", "composer.hide_preview": "הסתר תצוגה מקדימה", @@ -30,17 +30,17 @@ "composer.submit_and_lock": "אשר ונעל", "composer.toggle_dropdown": "הדלק/כבה את התפריט הנפתח", "composer.uploading": "העלאה %1", - "composer.formatting.bold": "Bold", - "composer.formatting.italic": "Italic", - "composer.formatting.list": "List", - "composer.formatting.strikethrough": "Strikethrough", - "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", - "composer.upload-picture": "Upload Image", - "composer.upload-file": "Upload File", - "composer.zen_mode": "Zen Mode", - "composer.select_category": "Select a category", - "bootbox.ok": "בסדר", + "composer.formatting.bold": "מודגש", + "composer.formatting.italic": "נטוי", + "composer.formatting.list": "רשימה", + "composer.formatting.strikethrough": "קו פוסל", + "composer.formatting.link": "לינק", + "composer.formatting.picture": "תמונה", + "composer.upload-picture": "העלה תמונה", + "composer.upload-file": "העלה קובץ", + "composer.zen_mode": "מצב זן", + "composer.select_category": "בחר קטגוריה", + "bootbox.ok": "אוקיי", "bootbox.cancel": "בטל", "bootbox.confirm": "אשר", "cover.dragging_title": "מיקום תמונת הנושא", diff --git a/public/language/he/notifications.json b/public/language/he/notifications.json index 71c8f78eb9..2f3ff9acea 100644 --- a/public/language/he/notifications.json +++ b/public/language/he/notifications.json @@ -10,15 +10,15 @@ "return_to": "חזור ל %1", "new_notification": "התראה חדשה", "you_have_unread_notifications": "יש לך התראות שלא נקראו.", - "all": "All", - "topics": "Topics", - "replies": "Replies", - "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "all": "הכל", + "topics": "נושאים", + "replies": "תגובות", + "chat": "צ'אטים", + "follows": "עוקבים", + "upvote": "הצבעות בעד", + "new-flags": "דיווחים חדשים", + "my-flags": "דיווחים שהוקצו עבורי", + "bans": "הרחקות", "new_message_from": "הודעה חדשה מ %1", "upvoted_your_post_in": "%1 הצביע בעד הפוסט שלך ב %2", "upvoted_your_post_in_dual": "%1 הצביע בעד הפוסט שלך ב %2", @@ -28,9 +28,9 @@ "user_flagged_post_in": "%1 דיווח על פוסט ב %2", "user_flagged_post_in_dual": "%1 ו%2 סימנו פוסט ב%3", "user_flagged_post_in_multiple": "%1 ו%2 נוספים סימנו פוסט ב%3", - "user_flagged_user": "%1 flagged a user profile (%2)", - "user_flagged_user_dual": "%1 and %2 flagged a user profile (%3)", - "user_flagged_user_multiple": "%1 and %2 others flagged a user profile (%3)", + "user_flagged_user": "%1 דיווח על משתמש (%2)", + "user_flagged_user_dual": "%1 ו - %2 דיווחו על משתמש (%3)", + "user_flagged_user_multiple": "%1 ו-%2 נוספים דיווחו על משתמש (%3)", "user_posted_to": "%1 פרסם תגובה ל: %2", "user_posted_to_dual": "%1 ו%2 הגיבו ל: %3", "user_posted_to_multiple": "%1 ו%2 אחרים הגיבו ל: %3", @@ -39,8 +39,8 @@ "user_started_following_you_dual": "%1 ו%1 התחילו לעקוב אחריך.", "user_started_following_you_multiple": "%1 ו%2 התחילו לעקוב אחריך.", "new_register": "%1 שלח בקשת הרשמה.", - "new_register_multiple": "There are %1 registration requests awaiting review.", - "flag_assigned_to_you": "Flag %1 has been assigned to you", + "new_register_multiple": "ישנן %1 בקשות הרשמה שמחכות לבדיקה.", + "flag_assigned_to_you": "דיווח %1 הוקצה עבורך", "email-confirmed": "כתובת המייל אושרה", "email-confirmed-message": "תודה שאישרת את כתובת המייל שלך. החשבון שלך פעיל כעת.", "email-confirm-error-message": "אירעה שגיאה בעת אישור המייל שלך. ייתכן כי הקוד היה שגוי או פג תוקף.", diff --git a/public/language/he/pages.json b/public/language/he/pages.json index 8832d5915a..854e1f33f4 100644 --- a/public/language/he/pages.json +++ b/public/language/he/pages.json @@ -6,20 +6,20 @@ "popular-month": "נושאים חמים החודש", "popular-alltime": "הנושאים החמים בכל הזמנים", "recent": "נושאים אחרונים", - "flagged-content": "Flagged Content", - "ip-blacklist": "IP Blacklist", + "flagged-content": "תוכן מדווח", + "ip-blacklist": "רשימת IP שחורה", "users/online": "משתמשים מחוברים", "users/latest": "משתמשים אחרונים", "users/sort-posts": "משתמשים עם המונה הגבוה ביותר", "users/sort-reputation": "משתמשים עם המוניטין הגבוה ביותר", "users/banned": "משתמשים מורחקים", - "users/most-flags": "Most flagged users", + "users/most-flags": "משתמשים שדווחו הכי הרבה", "users/search": "חיפוש משתמשים", "notifications": "התראות", "tags": "תגיות", "tag": "נושאים שתוייגו תחת \"%1\"", "register": "יצירת חשבון", - "registration-complete": "Registration complete", + "registration-complete": "ההרשמה הושלמה", "login": "התחבר לחשבונך", "reset": "איפוס סיסמה למשתמש", "categories": "קטגוריות", @@ -27,19 +27,19 @@ "group": "קבוצת %1", "chats": "הודעות פרטיות", "chat": "שלחו הודעה פרטית ל%1", - "flags": "Flags", - "flag-details": "Flag %1 Details", + "flags": "דיווחים", + "flag-details": "פרטי דיווח %1", "account/edit": "לערוך את \"%1\"", "account/edit/password": "עורך את הסיסמה של \"%1\"", "account/edit/username": "עורך את שם המשתמש של \"%1\"", "account/edit/email": "עורך את כתובת המייל של \"%1\"", - "account/info": "Account Info", + "account/info": "פרטי חשבון", "account/following": "אנשים ש%1 עוקב אחריהם", "account/followers": "אנשים שעוקבים אחרי %1", "account/posts": "הודעות שפורסמו על ידי %1", "account/topics": "נושאים שנוצרו על ידי %1", "account/groups": "הקבוצות של %1", - "account/bookmarks": "%1's Bookmarked Posts", + "account/bookmarks": "הפוסטים השמורים של %1", "account/settings": "הגדרות משתמש", "account/watched": "נושאים שנצפו על ידי %1", "account/upvoted": "פוסטים שהוצבעו לטובה על ידי %1", diff --git a/public/language/he/register.json b/public/language/he/register.json index 777e83302c..7ab0d02b84 100644 --- a/public/language/he/register.json +++ b/public/language/he/register.json @@ -1,6 +1,6 @@ { "register": "הרשם", - "cancel_registration": "Cancel Registration", + "cancel_registration": "בטל רישום", "help.email": "כברירת מחדל, כתובת האימייל שלך אינה חשופה למשתמשים אחרים", "help.username_restrictions": "שם משתמש ייחודי בין %1 ל %2 תווים. משתמשים אחרים יכולים לציין את שמך באמצעות @שם המשתמש שלך.", "help.minimum_password_length": "סיסמתך חייבת להיות לפחות באורך של %1 תווים.", @@ -16,8 +16,8 @@ "alternative_registration": "הרשם באמצעות...", "terms_of_use": "תנאי שימוש", "agree_to_terms_of_use": "אני מסכים לתנאי השימוש", - "terms_of_use_error": "You must agree to the Terms of Use", + "terms_of_use_error": "אתה מוכרח להסכים לתנאי השימוש", "registration-added-to-queue": "הבקשה שלך להרשמה נשלחה. תקבל בקרוב מייל אישור לכתובת האימייל שהכנסת כשמנהל יאשר את הבקשה.", - "interstitial.intro": "We require some additional information before we can create your account.", - "interstitial.errors-found": "We could not complete your registration:" + "interstitial.intro": "אנו דורשים מידע נוסף לפני שנוכל ליצור עבורך את החשבון.", + "interstitial.errors-found": "לא הצלחנו להשלים את הרישום שלך:" } \ No newline at end of file diff --git a/public/language/he/search.json b/public/language/he/search.json index cee9f0cc9a..2ce3728e7d 100644 --- a/public/language/he/search.json +++ b/public/language/he/search.json @@ -8,11 +8,11 @@ "posted-by": "פורסם על-ידי", "in-categories": "בקטגוריות", "search-child-categories": "חפש בתת קטגוריות", - "has-tags": "Has tags", + "has-tags": "עם תגיות", "reply-count": "כמות תגובות", "at-least": "לפחות", "at-most": "לכל היותר", - "relevance": "Relevance", + "relevance": "רלוונטיות", "post-time": "זמן הפוסט", "newer-than": "חדש מ", "older-than": "ישן מ", diff --git a/public/language/he/topic.json b/public/language/he/topic.json index e54bb502e6..e457ea82e1 100644 --- a/public/language/he/topic.json +++ b/public/language/he/topic.json @@ -13,8 +13,9 @@ "notify_me": "קבל התראה כאשר יש תגובות חדשות בנושא זה", "quote": "ציטוט", "reply": "תגובה", - "replies_to_this_post": "%1 Replies", - "last_reply_time": "Last reply", + "replies_to_this_post": "%1 תגובות", + "one_reply_to_this_post": "תגובה 1", + "last_reply_time": "תגובה אחרונה", "reply-as-topic": "הגב כנושא", "guest-login-reply": "התחבר כדי לפרסם תגובה", "edit": "עריכה", @@ -27,9 +28,9 @@ "share": "שתף", "tools": "כלים", "locked": "נעול", - "pinned": "Pinned", - "moved": "Moved", - "bookmark_instructions": "Click here to return to the last read post in this thread.", + "pinned": "נעוץ", + "moved": "הועבר", + "bookmark_instructions": "לחץ כאן לחזור לפוסט האחרון שקראת בנושא הזה.", "flag_title": "דווח על פוסט זה למנהל", "deleted_message": "נושא זה נמחק. רק משתמשים עם ההרשאות המתאימות יכולים לצפות בו.", "following_topic.message": "מעתה, תקבל הודעות כאשר מישהו יעלה פוסט לנושא זה.", @@ -44,20 +45,21 @@ "watch.title": "קבל התראה כאשר יש תגובות חדשות בנושא זה", "unwatch.title": "הפסק לעקוב אחר נושא זה", "share_this_post": "שתף פוסט זה", - "watching": "Watching", - "not-watching": "Not Watching", - "ignoring": "Ignoring", + "watching": "עוקב", + "not-watching": "לא עוקב", + "ignoring": "מתעלם", "watching.description": "Notify me of new replies.
Show topic in unread.", "not-watching.description": "Do not notify me of new replies.
Show topic in unread if category is not ignored.", "ignoring.description": "Do not notify me of new replies.
Do not show topic in unread.", "thread_tools.title": "כלי נושא", - "thread_tools.markAsUnreadForAll": "Mark unread for all", + "thread_tools.markAsUnreadForAll": "סמן הכל כלא נקרא", "thread_tools.pin": "נעץ נושא", "thread_tools.unpin": "הסר נעץ", "thread_tools.lock": "נעל נושא", "thread_tools.unlock": "הסר נעילה", "thread_tools.move": "הזז נושא", "thread_tools.move_all": "הזז הכל", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "שכפל נושא", "thread_tools.delete": "מחק נושא", "thread_tools.delete-posts": "מחק פוסטים", @@ -71,7 +73,6 @@ "post_restore_confirm": "אתה בטוח שאתה רוצה לשחזר את הפוסט הזה?", "post_purge_confirm": "אתה בטוח שאתה רוצה למחוק את הפוסט הזה?", "load_categories": "טוען קטגוריות", - "disabled_categories_note": "קטגוריות מבוטלות צבועות באפור", "confirm_move": "הזז", "confirm_fork": "שכפל", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "הזז פוסט", "post_moved": "הפוסט הועבר!", "fork_topic": "שכפל נושא", - "topic_will_be_moved_to": "נושא זה יועבר לקטגוריה", "fork_topic_instruction": "לחץ על הפוסטים שברצונך לשכפל", "fork_no_pids": "לא בחרת אף פוסט!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/he/unread.json b/public/language/he/unread.json index 75fd54d9bd..3f869f734a 100644 --- a/public/language/he/unread.json +++ b/public/language/he/unread.json @@ -9,5 +9,5 @@ "topics_marked_as_read.success": "נושאים שמסומנים כנקרא!", "all-topics": "כל הנושאים", "new-topics": "נושאים חדשים", - "watched-topics": "Watched Topics" + "watched-topics": "נושאים שאתה עוקב אחריהם" } \ No newline at end of file diff --git a/public/language/he/uploads.json b/public/language/he/uploads.json index 1622cb5693..887d827762 100644 --- a/public/language/he/uploads.json +++ b/public/language/he/uploads.json @@ -1,6 +1,6 @@ { - "uploading-file": "Uploading the file...", - "select-file-to-upload": "Select a file to upload!", - "upload-success": "File uploaded successfully!", - "maximum-file-size": "Maximum %1 kb" + "uploading-file": "מעלה את הקובץ...", + "select-file-to-upload": "בחר קובץ להעלאה!", + "upload-success": "הקובץ הועלה בהצלחה!", + "maximum-file-size": "מקסימום %1 קילובייט" } \ No newline at end of file diff --git a/public/language/he/user.json b/public/language/he/user.json index d5ad68e33b..bcb69d4c57 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -31,9 +31,9 @@ "signature": "חתימה", "birthday": "יום הולדת", "chat": "צ'אט", - "chat_with": "Continue chat with %1", - "new_chat_with": "Start new chat with %1", - "flag-profile": "Flag Profile", + "chat_with": "המשך צ'אט עם %1", + "new_chat_with": "התחל צ'אט עם %1", + "flag-profile": "דווח על משתמש", "follow": "עקוב", "unfollow": "הפסק לעקוב", "more": "עוד", @@ -42,7 +42,7 @@ "change_username": "שנה שם משתמש", "change_email": "שנה מייל", "edit": "ערוך", - "edit-profile": "Edit Profile", + "edit-profile": "ערוך פרופיל", "default_picture": "אייקון ברירת מחדל", "uploaded_picture": "התמונה הועלתה", "upload_new_picture": "העלה תמונה חדשה", @@ -60,14 +60,14 @@ "username_taken_workaround": "שם המשתמש שבחרת כבר תפוס, אז שינינו אותו מעט. שם המשתמש שלך כעת הוא %1", "password_same_as_username": "הסיסמה שלך זהה לשם המשתמש, אנא בחר סיסמה שונה.", "password_same_as_email": "הסיסמה שלך זהה לכתובת המייל שלך, אנא בחר סיסמה שונה.", - "weak_password": "Weak password.", + "weak_password": "סיסמה חלשה.", "upload_picture": "העלה תמונה", "upload_a_picture": "העלה תמונה", "remove_uploaded_picture": "מחק את התמונה שהועלתה", "upload_cover_picture": "העלה תמונת נושא", - "remove_cover_picture_confirm": "Are you sure you want to remove the cover picture?", - "crop_picture": "Crop picture", - "upload_cropped_picture": "Crop and upload", + "remove_cover_picture_confirm": "האם אתה בטוח שאתה רוצה למחוק את תמונת הקאבר?", + "crop_picture": "חתוך תמונה", + "upload_cropped_picture": "חתוך והעלה", "settings": "הגדרות", "show_email": "פרסם את כתובת האימייל שלי", "show_fullname": "הצג את שמי המלא", @@ -95,21 +95,21 @@ "topics_per_page": "כמות נושאים בעמוד", "posts_per_page": "כמות פוסטים בעמוד", "notification_sounds": "נגן סאונד כשמתקבלת התראה", - "notifications_and_sounds": "Notifications & Sounds", - "incoming-message-sound": "Incoming message sound", - "outgoing-message-sound": "Outgoing message sound", - "notification-sound": "Notification sound", - "no-sound": "No sound", + "notifications_and_sounds": "התראות וצלילים", + "incoming-message-sound": "צליל הודעה נכנסת", + "outgoing-message-sound": "צליל הודעה יוצאת", + "notification-sound": "צליל הודעה", + "no-sound": "ללא צליל", "browsing": "הגדרות צפייה", "open_links_in_new_tab": "פתח קישורים חיצוניים בכרטיסייה חדשה", "enable_topic_searching": "הפעל חיפוש בתוך נושא", "topic_search_help": "אם מאופשר, החיפוש בתוך הנושא יעקוף את שיטת החיפוש של הדפדפן, ויאפשר לך לחפש בכל הנושא - ולא רק במה שמוצג על המסך", - "delay_image_loading": "Delay Image Loading", - "image_load_delay_help": "If enabled, images in topics will not load until they are scrolled into view", - "scroll_to_my_post": "After posting a reply, show the new post", - "follow_topics_you_reply_to": "Watch topics that you reply to", - "follow_topics_you_create": "Watch topics you create", - "grouptitle": "Group Title", + "delay_image_loading": "עכב טעינת תמונות", + "image_load_delay_help": "אם מאופשר, תמונות שנמצאות בתחתית העמוד יטענו רק כאשר תגלול אליהן", + "scroll_to_my_post": "הצג את הפוסט לאחר פרסום התגובה", + "follow_topics_you_reply_to": "עקוב אחר נושאים שהגבת עליהם", + "follow_topics_you_create": "עקוב אחר נושאים שפרסמת", + "grouptitle": "כותרת הקבוצה", "no-group-title": "ללא כותרת לקבוצה", "select-skin": "בחר מראה", "select-homepage": "בחר דף בית", @@ -120,17 +120,17 @@ "sso.title": "שירות יחיד להתחברות", "sso.associated": "משוייך עם", "sso.not-associated": "לחץ כאן כדי לשייך", - "info.latest-flags": "Latest Flags", - "info.no-flags": "No Flagged Posts Found", - "info.ban-history": "Recent Ban History", - "info.no-ban-history": "This user has never been banned", - "info.banned-until": "Banned until %1", - "info.banned-permanently": "Banned permanently", + "info.latest-flags": "דיווחים אחרונים", + "info.no-flags": "לא נמצאו פוסטים שמשתמשים דיווחו עליהם", + "info.ban-history": "היסטוריית הרחקות", + "info.no-ban-history": "משתמש זה מעולם לא הורחק", + "info.banned-until": "הורחק עד %1", + "info.banned-permanently": "הורחק לצמיתות", "info.banned-reason-label": "סיבה", - "info.banned-no-reason": "No reason given.", - "info.username-history": "Username History", - "info.email-history": "Email History", - "info.moderation-note": "Moderation Note", - "info.moderation-note.success": "Moderation note saved", - "info.moderation-note.add": "Add note" + "info.banned-no-reason": "לא ניתנה סיבה.", + "info.username-history": "היסטוריית שם משתמש", + "info.email-history": "היסטוריית אימייל", + "info.moderation-note": "הערת מודרטור", + "info.moderation-note.success": "הערת מודרטור נשמרה", + "info.moderation-note.add": "הוסף הערה" } \ No newline at end of file diff --git a/public/language/he/users.json b/public/language/he/users.json index b4c068bd33..6dbd058eae 100644 --- a/public/language/he/users.json +++ b/public/language/he/users.json @@ -2,7 +2,7 @@ "latest_users": "משתמשים אחרונים", "top_posters": "מפרסמים הכי הרבה", "most_reputation": "המוניטין הגבוה ביותר", - "most_flags": "Most Flags", + "most_flags": "הכי הרבה דיווחי משתמשים", "search": "חיפוש", "enter_username": "הכנס שם משתמש לחיפוש", "load_more": "טען עוד", diff --git a/public/language/hr/admin/appearance/customise.json b/public/language/hr/admin/appearance/customise.json index 0809a8e20c..a283a153aa 100644 --- a/public/language/hr/admin/appearance/customise.json +++ b/public/language/hr/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Uobičajno zaglavlje", "custom-header.description": "Unesite Vaš HTML ovdje(npr. JavaScript Meta Tags itd.)koji će biti dodani <head> sekciji marže Vašeg foruma.", - "custom-header.enable": "Omogući uobičajeno zaglavlje" + "custom-header.enable": "Omogući uobičajeno zaglavlje", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/hr/admin/development/info.json b/public/language/hr/admin/development/info.json index 0d7b3eae8e..6921af57c2 100644 --- a/public/language/hr/admin/development/info.json +++ b/public/language/hr/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - Vi ste %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "Domaćin", "pid": "pid", "nodejs": "nodejs", "online": "Na mreži", "git": "git", + "memory": "memory", "load": "učitaj", "uptime": "uptime", diff --git a/public/language/hr/admin/general/languages.json b/public/language/hr/admin/general/languages.json index fc0d958019..a20b3c705d 100644 --- a/public/language/hr/admin/general/languages.json +++ b/public/language/hr/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Postavke jezika", "description": "Zadani jezik odlučuje o postavkama jezika za sve korisnike foruma.
.Korisnici mogu sami odabrati jezik na stranici postavki jezika.", - "default-language": "Zadani jezik" + "default-language": "Zadani jezik", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/hr/admin/manage/categories.json b/public/language/hr/admin/manage/categories.json index f87dfb28f8..69ca196181 100644 --- a/public/language/hr/admin/manage/categories.json +++ b/public/language/hr/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Obična klasa", "num-recent-replies": "# nedavnih objava", "ext-link": "Vanjska poveznica", + "is-section": "Treat this category as a section", "upload-image": "Učitaj sliku", "delete-image": "Ukloni", "category-image": "Slika kategorije", diff --git a/public/language/hr/admin/settings/user.json b/public/language/hr/admin/settings/user.json index 7a012120b0..46f7b8bac3 100644 --- a/public/language/hr/admin/settings/user.json +++ b/public/language/hr/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maksimalan broj pozivnica po korisniku", "max-invites": "Maksimalan broj pozivnica po korisniku", "max-invites-help": "0 bez restrikcija. Administrator ima neograničeno pozivnica
Primjenjivo samo za \"poziv na forum\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimalna dužina korisničkog imena", "max-username-length": "Maksimalna dužina korisničkog imena", "min-password-length": "Minimalna dužina lozinke", diff --git a/public/language/hr/error.json b/public/language/hr/error.json index 1a7b56feeb..55ccfa9c97 100644 --- a/public/language/hr/error.json +++ b/public/language/hr/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Nevažeći podaci", + "invalid-json": "Invalid JSON", "not-logged-in": "Izgleda da niste prijavljeni", "account-locked": "Vaš račun je privremeno blokiran", "search-requires-login": "Pretraga zahtijeva prijavu - prijavite se ili se registrirajte.", @@ -12,6 +13,7 @@ "invalid-title": "Netočan naslov!", "invalid-user-data": "Netočni korisnički podatci", "invalid-password": "Netočna lozinka", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Upišite oboje, korisničko ime i lozinku", "invalid-search-term": "Netočan upit pretraživanja", "csrf-invalid": "Nismo Vas uspijeli prijaviti, najvjerovatnije zbog istekle sesije. Molimo pokušajte ponovno", diff --git a/public/language/hr/global.json b/public/language/hr/global.json index 8d02d5bc58..7a79b3deb3 100644 --- a/public/language/hr/global.json +++ b/public/language/hr/global.json @@ -103,5 +103,7 @@ "cookies.message": "Ova stranica koristi kolačiće kako bi osigurala najbolje korisničko iskustvo.", "cookies.accept": "Shvaćam!", "cookies.learn_more": "Saznaj više", - "edited": "Uređeno" + "edited": "Uređeno", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/hr/topic.json b/public/language/hr/topic.json index 45b25f3e6c..98a6210428 100644 --- a/public/language/hr/topic.json +++ b/public/language/hr/topic.json @@ -14,6 +14,7 @@ "quote": "Citat", "reply": "Odgovor", "replies_to_this_post": "%1 je odgovorio", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Zadnji odgovor", "reply-as-topic": "Odgovori kao temu", "guest-login-reply": "Prijavi se za objavu", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Odključaj temu", "thread_tools.move": "Premjesti temu", "thread_tools.move_all": "Premjesti sve", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Dupliraj temu", "thread_tools.delete": "Obriši temu", "thread_tools.delete-posts": "Obriši objavu", @@ -71,7 +73,6 @@ "post_restore_confirm": "Sigurni ste da želite povratiti ovu objavu?", "post_purge_confirm": "Sigurni ste da želite odbaciti ovu objavu?", "load_categories": "Učitavam kategorije", - "disabled_categories_note": "Onemogućene kategorije su izbljeđene", "confirm_move": "Pomakni", "confirm_fork": "Dupliraj", "bookmark": "Zabilježi", @@ -83,7 +84,6 @@ "move_post": "Pomakni objavu", "post_moved": "Objava pomaknuta!", "fork_topic": "Dupliraj temu", - "topic_will_be_moved_to": "Ova tem će biti pomaknuta u kategoriju", "fork_topic_instruction": "Označi objave koje želite duplirati", "fork_no_pids": "Objave nisu odabrane!", "fork_pid_count": "%1 objava odabrana", diff --git a/public/language/hu/admin/appearance/customise.json b/public/language/hu/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/hu/admin/appearance/customise.json +++ b/public/language/hu/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/hu/admin/development/info.json b/public/language/hu/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/hu/admin/development/info.json +++ b/public/language/hu/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/hu/admin/general/languages.json b/public/language/hu/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/hu/admin/general/languages.json +++ b/public/language/hu/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/hu/admin/manage/categories.json b/public/language/hu/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/hu/admin/manage/categories.json +++ b/public/language/hu/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/hu/admin/settings/user.json b/public/language/hu/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/hu/admin/settings/user.json +++ b/public/language/hu/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 2741e13087..14e34a907c 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Érvénytelen adat", + "invalid-json": "Invalid JSON", "not-logged-in": "Úgy tűnik, nem vagy bejelentkezve.", "account-locked": "A fiókod ideiglenesen zárolva lett.", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Érvénytelen cím!", "invalid-user-data": "Érvénytelen felhasználói adatok", "invalid-password": "Érvénytelen jelszó", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Kérlek adj meg egy felhasználónevet és egy jelszót", "invalid-search-term": "Érvénytelen keresési feltétel", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/hu/global.json b/public/language/hu/global.json index c05367f938..8ccd973fbe 100644 --- a/public/language/hu/global.json +++ b/public/language/hu/global.json @@ -103,5 +103,7 @@ "cookies.message": "A weboldal sütiket használ, a legjobb weboldalas élmény érdekében.", "cookies.accept": "Értem!", "cookies.learn_more": "Tudnivalók", - "edited": "Szerkesztett" + "edited": "Szerkesztett", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/hu/topic.json b/public/language/hu/topic.json index 1a67ce9b46..2643558f3d 100644 --- a/public/language/hu/topic.json +++ b/public/language/hu/topic.json @@ -14,6 +14,7 @@ "quote": "Idéz", "reply": "Válasz", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Topik feloldása", "thread_tools.move": "Topik mozgatása", "thread_tools.move_all": "Move All", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Topik szétszedése", "thread_tools.delete": "Topik törlése", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Are you sure you want to restore this post?", "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Kategóriák betöltése", - "disabled_categories_note": "Kikapcsolt kategóriák kiszürkülve", "confirm_move": "Áthelyezés", "confirm_fork": "Szétszedés", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Hozzászólás áthelyezése", "post_moved": "Post moved!", "fork_topic": "Topik szétszedése", - "topic_will_be_moved_to": "Ez a téma ebbe a kategóriába lesz mozgatva", "fork_topic_instruction": "Klikkelj azokra a hozzászólásokra, amiket szét akarsz szedni", "fork_no_pids": "Nincs hozzászólás kiválasztva!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/id/admin/appearance/customise.json b/public/language/id/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/id/admin/appearance/customise.json +++ b/public/language/id/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/id/admin/development/info.json b/public/language/id/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/id/admin/development/info.json +++ b/public/language/id/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/id/admin/general/languages.json b/public/language/id/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/id/admin/general/languages.json +++ b/public/language/id/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/id/admin/manage/categories.json b/public/language/id/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/id/admin/manage/categories.json +++ b/public/language/id/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/id/admin/settings/user.json b/public/language/id/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/id/admin/settings/user.json +++ b/public/language/id/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/id/error.json b/public/language/id/error.json index 18c53d69f5..9b7a820a07 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Data Salah", + "invalid-json": "Invalid JSON", "not-logged-in": "Kamu terlihat belum login", "account-locked": "Akun kamu dikunci sementara", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Judul Salah", "invalid-user-data": "Data Pengguna Salah", "invalid-password": "Password Salah", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Mohon spesifikasikan username dan password", "invalid-search-term": "Kata pencarian salah", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/id/global.json b/public/language/id/global.json index a2169ab30c..6301423f5f 100644 --- a/public/language/id/global.json +++ b/public/language/id/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/id/topic.json b/public/language/id/topic.json index abb48888eb..745de176bf 100644 --- a/public/language/id/topic.json +++ b/public/language/id/topic.json @@ -14,6 +14,7 @@ "quote": "Kutip", "reply": "Balas", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in untuk membalas", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Lepas Topik", "thread_tools.move": "Pindah Topik", "thread_tools.move_all": "Pindah Semua", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Cabangkan Topik", "thread_tools.delete": "Hapus Topik", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Kamu yakin ingin mengembalikan posting ini?", "post_purge_confirm": "Kamu yakin ingin memusnahkan posting ini?", "load_categories": "Memuat Kategori", - "disabled_categories_note": "Indikator Kategori yang Ditiadakan keabu-abuan", "confirm_move": "Pindah", "confirm_fork": "Cabangkan", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Pindahkan Posting", "post_moved": "Posting dipindahkan!", "fork_topic": "Cabangkan Topik", - "topic_will_be_moved_to": "Topik ini akan dipindahkan ke kategori", "fork_topic_instruction": "Klik posting yang kamu ingin cabangkan", "fork_no_pids": "Tidak ada posting yang dipilih!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/it/admin/appearance/customise.json b/public/language/it/admin/appearance/customise.json index c97943fd6f..c5eb9568ef 100644 --- a/public/language/it/admin/appearance/customise.json +++ b/public/language/it/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Intestazione Personalizzata", "custom-header.description": "Inserisci l' HTML personalizzato qui (es. JavaScript, Meta Tags, ecc.), verrà attaccato al codice <head> sezione del markup del tuo forum", - "custom-header.enable": "Abilita l'Intestazione Personalizzata" + "custom-header.enable": "Abilita l'Intestazione Personalizzata", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/it/admin/development/info.json b/public/language/it/admin/development/info.json index 38e991ebd6..0b3f1c8d37 100644 --- a/public/language/it/admin/development/info.json +++ b/public/language/it/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Informazione - Tu sei su %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "carica", "uptime": "tempo di caricamento", diff --git a/public/language/it/admin/general/languages.json b/public/language/it/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/it/admin/general/languages.json +++ b/public/language/it/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/it/admin/manage/categories.json b/public/language/it/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/it/admin/manage/categories.json +++ b/public/language/it/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/it/admin/settings/user.json b/public/language/it/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/it/admin/settings/user.json +++ b/public/language/it/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/it/email.json b/public/language/it/email.json index 1cf2f5ad7b..48dbc3fd50 100644 --- a/public/language/it/email.json +++ b/public/language/it/email.json @@ -6,7 +6,7 @@ "greeting_with_name": "Ciao %1", "welcome.text1": "Grazie per esserti registrato su %1!", "welcome.text2": "Per attivare completamente il tuo account dobbiamo verificare che sei il proprietario dell'indirizzo email con cui ti sei registrato.", - "welcome.text3": "Un Amministratora ha accettato la tua registrazione. Adesso puoi collegarti con il tuo nome utente/ password.", + "welcome.text3": "Un amministratore ha accettato la tua registrazione. Adesso puoi collegarti con il tuo nome utente/password.", "welcome.cta": "Clicca qui per confermare il tuo indirizzo email", "invitation.text1": "%1 ti ha invitato a entrare in %2", "invitation.ctr": "Clicca qui per creare il tuo account.", diff --git a/public/language/it/error.json b/public/language/it/error.json index e87e872b70..c6a8f0af3a 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Dati non validi", + "invalid-json": "Invalid JSON", "not-logged-in": "Non sembri essere loggato.", "account-locked": "Il tuo account è stato bloccato temporaneamente", "search-requires-login": "La ricerca richiede un account! Si prega di loggarsi o registrarsi!", @@ -12,6 +13,7 @@ "invalid-title": "Titolo non valido", "invalid-user-data": "Dati Utente non validi", "invalid-password": "Password non valida", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Si prega di specificare sia un nome utente che una password", "invalid-search-term": "Termine di ricerca non valido", "csrf-invalid": "Non siamo riusciti a farti connettere, probabilmente perché la sessione è scaduta. Per favore riprova.", diff --git a/public/language/it/global.json b/public/language/it/global.json index b0599c51d0..7325219082 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -103,5 +103,7 @@ "cookies.message": "Questo sito utilizza i cookie per garantirti la miglior esperienza di navigazione possibile", "cookies.accept": "Ho capito!", "cookies.learn_more": "Scopri di più", - "edited": "Modificato" + "edited": "Modificato", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index 70b794bdc6..11c9941543 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -10,15 +10,15 @@ "return_to": "Ritorna a %1", "new_notification": "Nuova Notifica", "you_have_unread_notifications": "Hai notifiche non lette.", - "all": "All", + "all": "Tutte", "topics": "Topics", - "replies": "Replies", - "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "replies": "Risposte", + "chat": "Chat", + "follows": "Segui", + "upvote": "Voti", + "new-flags": "Nuove segnalazioni", + "my-flags": "Segnalazioni assegnate a me", + "bans": "Espulsioni", "new_message_from": "Nuovo messaggio da %1", "upvoted_your_post_in": "%1 ha votato positivamente il tuo post in %2.", "upvoted_your_post_in_dual": "%1 e %2 hanno apprezzato il tuo post in %3.", diff --git a/public/language/it/topic.json b/public/language/it/topic.json index c7c5bfbaca..e9540a5b89 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -14,6 +14,7 @@ "quote": "Cita", "reply": "Rispondi", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Topic risposta", "guest-login-reply": "Effettua il Log in per rispondere", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Sblocca Discussione", "thread_tools.move": "Sposta Discussione", "thread_tools.move_all": "Sposta Tutto", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Dividi Discussione", "thread_tools.delete": "Elimina Discussione", "thread_tools.delete-posts": "Cancella post", @@ -71,7 +73,6 @@ "post_restore_confirm": "Sei sicuro di voler ripristinare questo post?", "post_purge_confirm": "Sei sicuro di voler svuotare questo post?", "load_categories": "Caricamento Categorie", - "disabled_categories_note": "Le Categorie disabilitate sono in grigio", "confirm_move": "Sposta", "confirm_fork": "Dividi", "bookmark": "Favorito", @@ -83,7 +84,6 @@ "move_post": "Sposta Post", "post_moved": "Post spostato!", "fork_topic": "Dividi Discussione", - "topic_will_be_moved_to": "Questa discussione verrà spostata nella categoria", "fork_topic_instruction": "Clicca sui post che vuoi dividere", "fork_no_pids": "Nessun post selezionato!", "fork_pid_count": "%1 post selezionati", diff --git a/public/language/ja/admin/appearance/customise.json b/public/language/ja/admin/appearance/customise.json index fc00e1ad92..c2e1f7cbbc 100644 --- a/public/language/ja/admin/appearance/customise.json +++ b/public/language/ja/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "カスタムヘッダー", "custom-header.description": "カスタムしたHTMLを入力してください(例. JavaScript, メタタグなど)。これはフォーラムのマークアップの<head>に追加されます。", - "custom-header.enable": "カスタムヘッダーを有効にする" + "custom-header.enable": "カスタムヘッダーを有効にする", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/ja/admin/development/info.json b/public/language/ja/admin/development/info.json index 9169cb20d6..c6a7837667 100644 --- a/public/language/ja/admin/development/info.json +++ b/public/language/ja/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "お知らせ - あなたは%1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "ホスト", "pid": "pid", "nodejs": "nodejs", "online": "オンライン", "git": "git", + "memory": "memory", "load": "ロード", "uptime": "稼働時間", diff --git a/public/language/ja/admin/general/dashboard.json b/public/language/ja/admin/general/dashboard.json index ed544afb51..124d26c394 100644 --- a/public/language/ja/admin/general/dashboard.json +++ b/public/language/ja/admin/general/dashboard.json @@ -20,12 +20,12 @@ "stats.all": "全て", "updates": "更新", - "running-version": "NodeBB v %1 を実行しています。", + "running-version": "NodeBB v%1 を実行しています。", "keep-updated": "常に最新のセキュリティパッチとバグ修正のためにNodeBBが最新であることを確認してください。", - "up-to-date": "

あなたは最新の状態です。 ", + "up-to-date": "

あなたは最新の状態です。

", "upgrade-available": "

新しいバージョン (v%1) がリリースされました。NodeBBのアップグレードを検討してください。

", "prerelease-upgrade-available": "

これはNodeBBの旧リリースのバージョンです。新しいバージョン(v%1)がリリースされました。 NodeBBのアップグレードを検討してください。", - "prerelease-warning": "

これはNodeBBのプレリリース版です。意図しないバグが発生することがあります。

", + "prerelease-warning": "

これはNodeBBのプレリリース版です。意図しないバグが発生することがあります。

", "running-in-development": "フォーラムが開発モードで動作しています。フォーラムの動作が脆弱かもしれませんので、管理者に問い合わせてください。", "notices": "通知", diff --git a/public/language/ja/admin/general/languages.json b/public/language/ja/admin/general/languages.json index 1d17c27bee..095dcd8a0f 100644 --- a/public/language/ja/admin/general/languages.json +++ b/public/language/ja/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "言語設定", "description": "デフォルトの言語は、フォーラムにアクセスしているすべてのユーザーの言語表示を決定します。
個々のユーザーは、アカウント設定ページでデフォルトの言語を上書きできます。", - "default-language": "デフォルトの言語" + "default-language": "デフォルトの言語", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/ja/admin/manage/categories.json b/public/language/ja/admin/manage/categories.json index d045070a3c..2d4f6ddab1 100644 --- a/public/language/ja/admin/manage/categories.json +++ b/public/language/ja/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "カスタムClass", "num-recent-replies": "# 最近の返信数", "ext-link": "外部リンク", + "is-section": "Treat this category as a section", "upload-image": "画像をアップロード", "delete-image": "削除", "category-image": "カテゴリ画像", diff --git a/public/language/ja/admin/settings/user.json b/public/language/ja/admin/settings/user.json index 1048a1863c..8bdd9fdb73 100644 --- a/public/language/ja/admin/settings/user.json +++ b/public/language/ja/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "ユーザーごとの最大招待数", "max-invites": "ユーザーごとの最大招待数", "max-invites-help": "無制限の場合は0です。管理者は無限の招待を受ける
「招待のみ」にのみ適用されます", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "ユーザー名の最小文字数", "max-username-length": "ユーザー名の最大文字数", "min-password-length": "パスワードの最小文字数", diff --git a/public/language/ja/error.json b/public/language/ja/error.json index 52df59a6d4..f450eed636 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -1,5 +1,6 @@ { "invalid-data": "無効なデータ", + "invalid-json": "Invalid JSON", "not-logged-in": "ログインしていません。", "account-locked": "あなたのアカウントは一時的にロックされています", "search-requires-login": "検索するにはアカウントが必要です - ログインするかアカウントを作成してください。", @@ -12,6 +13,7 @@ "invalid-title": "無効なタイトル!", "invalid-user-data": "無効なユーザーデータ", "invalid-password": "無効なパスワード", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "ユーザー名とパスワードの両方を指定してください", "invalid-search-term": "無効な検索ワード", "csrf-invalid": "セッションの期限切れと思われるため、私達はあなたのログイン状態を確認できませんでした。もう一度お試しください。", diff --git a/public/language/ja/global.json b/public/language/ja/global.json index 68b963f55c..89b71b1856 100644 --- a/public/language/ja/global.json +++ b/public/language/ja/global.json @@ -103,5 +103,7 @@ "cookies.message": "このWEBサイトは、心地良くご使用頂くためにクッキーを使用しています。", "cookies.accept": "了解!", "cookies.learn_more": "もっと詳しく", - "edited": "編集されました" + "edited": "編集されました", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json index 0719e1d021..0e255db706 100644 --- a/public/language/ja/topic.json +++ b/public/language/ja/topic.json @@ -14,6 +14,7 @@ "quote": "引用", "reply": "返信", "replies_to_this_post": "%1 件の返信", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "最後の返信", "reply-as-topic": "スレッドとして返信する", "guest-login-reply": "投稿するのにログインして下さい", @@ -58,6 +59,7 @@ "thread_tools.unlock": "スレッドをアンロック", "thread_tools.move": "スレッドを移動", "thread_tools.move_all": "すべてを移動", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "スレッドをフォーク", "thread_tools.delete": "スレッドを削除", "thread_tools.delete-posts": "投稿を削除します", @@ -71,7 +73,6 @@ "post_restore_confirm": "本当にこの投稿を元に戻しますか?", "post_purge_confirm": "本当にこの投稿を切り離しますか?", "load_categories": "板をローディング中...", - "disabled_categories_note": "使用不可の板はグレーに表示されます。", "confirm_move": "移動", "confirm_fork": "フォーク", "bookmark": "ブックマーク", @@ -83,7 +84,6 @@ "move_post": "投稿を移動", "post_moved": "投稿を移動しました!", "fork_topic": "スレッドをフォーク", - "topic_will_be_moved_to": "スレッドはこちらのカテゴリへ移動", "fork_topic_instruction": "フォークしたい投稿をクリックして", "fork_no_pids": "投稿が選択されていません!", "fork_pid_count": "%1 投稿(s)が選択されました", diff --git a/public/language/ko/admin/admin.json b/public/language/ko/admin/admin.json index 9c01f56006..866b8eee28 100644 --- a/public/language/ko/admin/admin.json +++ b/public/language/ko/admin/admin.json @@ -1,7 +1,7 @@ { - "alert.confirm-reload": "Are you sure you wish to reload NodeBB?", - "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", + "alert.confirm-reload": "NodeBB를 새로 고침 하시겠습니까?", + "alert.confirm-restart": "NodeBB를 다시 시작하시겠습니까?", - "acp-title": "%1 | NodeBB Admin Control Panel", - "settings-header-contents": "Contents" + "acp-title": "%1 | NodeBB 관리자 제어판", + "settings-header-contents": "컨텐츠" } \ No newline at end of file diff --git a/public/language/ko/admin/advanced/cache.json b/public/language/ko/admin/advanced/cache.json index 5a954f1232..5fb24659e6 100644 --- a/public/language/ko/admin/advanced/cache.json +++ b/public/language/ko/admin/advanced/cache.json @@ -1,11 +1,11 @@ { - "post-cache": "Post Cache", - "posts-in-cache": "Posts in Cache", - "average-post-size": "Average Post Size", - "length-to-max": "Length / Max", - "percent-full": "%1% Full", - "post-cache-size": "Post Cache Size", - "items-in-cache": "Items in Cache", - "control-panel": "Control Panel", - "update-settings": "Update Cache Settings" + "post-cache": "포스트 캐시", + "posts-in-cache": "캐시된 포스트", + "average-post-size": "평균 포스트 크기", + "length-to-max": "길이 / 최대", + "percent-full": "%1%꽉참", + "post-cache-size": "포스트 캐시 크기", + "items-in-cache": "캐시된 항목들", + "control-panel": "제어판", + "update-settings": "캐시 설정 변경" } \ No newline at end of file diff --git a/public/language/ko/admin/advanced/database.json b/public/language/ko/admin/advanced/database.json index b88ca6fc82..ee38140153 100644 --- a/public/language/ko/admin/advanced/database.json +++ b/public/language/ko/admin/advanced/database.json @@ -1,36 +1,36 @@ { - "x-b": "%1 b", - "x-mb": "%1 mb", - "x-gb": "%1 gb", - "uptime-seconds": "Uptime in Seconds", - "uptime-days": "Uptime in Days", + "x-b": "%1 B", + "x-mb": "%1 MB", + "x-gb": "%1 GB", + "uptime-seconds": "Uptime (초)", + "uptime-days": "Uptime (일)", "mongo": "Mongo", - "mongo.version": "MongoDB Version", + "mongo.version": "MongoDB 버젼", "mongo.storage-engine": "Storage Engine", "mongo.collections": "Collections", - "mongo.objects": "Objects", - "mongo.avg-object-size": "Avg. Object Size", - "mongo.data-size": "Data Size", - "mongo.storage-size": "Storage Size", - "mongo.index-size": "Index Size", - "mongo.file-size": "File Size", + "mongo.objects": "객체", + "mongo.avg-object-size": "평균 객체 크기", + "mongo.data-size": "데이터 크기", + "mongo.storage-size": "저장공간 크기", + "mongo.index-size": "인덱스 크기", + "mongo.file-size": "파일 크기", "mongo.resident-memory": "Resident Memory", "mongo.virtual-memory": "Virtual Memory", "mongo.mapped-memory": "Mapped Memory", "mongo.raw-info": "MongoDB Raw Info", "redis": "Redis", - "redis.version": "Redis Version", - "redis.connected-clients": "Connected Clients", - "redis.connected-slaves": "Connected Slaves", - "redis.blocked-clients": "Blocked Clients", - "redis.used-memory": "Used Memory", + "redis.version": "Redis 버젼", + "redis.connected-clients": "연결된 클라이언트", + "redis.connected-slaves": "연결된 slaves", + "redis.blocked-clients": "차단된 클라이언트", + "redis.used-memory": "사용된 메모리", "redis.memory-frag-ratio": "Memory Fragmentation Ratio", - "redis.total-connections-recieved": "Total Connections Received", - "redis.total-commands-processed": "Total Commands Processed", - "redis.iops": "Instantaneous Ops. Per Second", - "redis.keyspace-hits": "Keyspace Hits", - "redis.keyspace-misses": "Keyspace Misses", + "redis.total-connections-recieved": "받은 총 커넥션 수", + "redis.total-commands-processed": "처리된 총 커맨드 수", + "redis.iops": "초당 Instantaneous Ops", + "redis.keyspace-hits": "Keyspace 히트", + "redis.keyspace-misses": "Keyspace 미스", "redis.raw-info": "Redis Raw Info" } \ No newline at end of file diff --git a/public/language/ko/admin/advanced/errors.json b/public/language/ko/admin/advanced/errors.json index 546f0f1508..af520c8d00 100644 --- a/public/language/ko/admin/advanced/errors.json +++ b/public/language/ko/admin/advanced/errors.json @@ -1,14 +1,14 @@ { - "figure-x": "Figure %1", - "error-events-per-day": "%1 events per day", - "error.404": "404 Not Found", - "error.503": "503 Service Unavailable", - "manage-error-log": "Manage Error Log", - "export-error-log": "Export Error Log (CSV)", - "clear-error-log": "Clear Error Log", - "route": "Route", - "count": "Count", - "no-routes-not-found": "Hooray! No 404 errors!", - "clear404-confirm": "Are you sure you wish to clear the 404 error logs?", - "clear404-success": "\"404 Not Found\" errors cleared" + "figure-x": "그림 %1", + "error-events-per-day": "하루당 %1개의 이벤트 ", + "error.404": "404 찾을 수 없음", + "error.503": "503 서비스를 사용할 수 없음", + "manage-error-log": "오류 로그 관리", + "export-error-log": "오류 기록 Export (CSV)", + "clear-error-log": "오류 기록 지우기", + "route": "라우트", + "count": "카운트", + "no-routes-not-found": "만세! 404 오류 없음!", + "clear404-confirm": "404 오류 기록을 지우시겠습니까?", + "clear404-success": "\"404 찾을 수 없음\" 오류 삭제 완료" } \ No newline at end of file diff --git a/public/language/ko/admin/advanced/events.json b/public/language/ko/admin/advanced/events.json index 766eb5e951..79b7b76163 100644 --- a/public/language/ko/admin/advanced/events.json +++ b/public/language/ko/admin/advanced/events.json @@ -1,6 +1,6 @@ { - "events": "Events", - "no-events": "There are no events", - "control-panel": "Events Control Panel", - "delete-events": "Delete Events" + "events": "이벤트", + "no-events": "이벤트가 없습니다", + "control-panel": "이벤트 제어판", + "delete-events": "이벤트 삭제" } \ No newline at end of file diff --git a/public/language/ko/admin/advanced/logs.json b/public/language/ko/admin/advanced/logs.json index b9de400e1c..9d327241ad 100644 --- a/public/language/ko/admin/advanced/logs.json +++ b/public/language/ko/admin/advanced/logs.json @@ -1,7 +1,7 @@ { - "logs": "Logs", - "control-panel": "Logs Control Panel", - "reload": "Reload Logs", - "clear": "Clear Logs", - "clear-success": "Logs Cleared!" + "logs": "로그", + "control-panel": "로그 제어판", + "reload": "로그 리로드", + "clear": "로그 지우기", + "clear-success": "로그가 지워졌습니다!" } \ No newline at end of file diff --git a/public/language/ko/admin/appearance/customise.json b/public/language/ko/admin/appearance/customise.json index 767d443e29..37a5dc71a4 100644 --- a/public/language/ko/admin/appearance/customise.json +++ b/public/language/ko/admin/appearance/customise.json @@ -1,9 +1,12 @@ { - "custom-css": "Custom CSS", - "custom-css.description": "Enter your own CSS declarations here, which will be applied after all other styles.", - "custom-css.enable": "Enable Custom CSS", + "custom-css": "사용자 정의 CSS", + "custom-css.description": "사용자 정의 CSS를 이곳에 입력하세요. 이 스타일들은 맨 마지막에 적용됩니다.", + "custom-css.enable": "사용자 정의 CSS 허용", - "custom-header": "Custom Header", - "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header": "사용자 정의 헤더", + "custom-header.description": "이곳에 사용자 정의 HTML을 입력하십시오 (예. JavaScript, Meta Tags, 등등). 당신의 포럼의 <head>1 섹션에 추가됩니다.", + "custom-header.enable": "사용자 정의 헤더 허용", + + "custom-css.livereload": "실시간 새로 고침 허용", + "custom-css.livereload.description": "세이브를 누를 때마다 당신의 계정에 속한 디바이스의 모든 세션들이 새로고침 되게 하시려면 이것을 활성화하세요." } \ No newline at end of file diff --git a/public/language/ko/admin/appearance/skins.json b/public/language/ko/admin/appearance/skins.json index 4db6fbdd8a..46ff0c9730 100644 --- a/public/language/ko/admin/appearance/skins.json +++ b/public/language/ko/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "Loading Skins...", - "homepage": "Homepage", - "select-skin": "Select Skin", - "current-skin": "Current Skin", - "skin-updated": "Skin Updated", - "applied-success": "%1 skin was succesfully applied", - "revert-success": "Skin reverted to base colours" + "loading": "스킨 로딩 중...", + "homepage": "홈페이지", + "select-skin": "스킨 선택", + "current-skin": "현재 스킨", + "skin-updated": "스킨 업데이트 됨", + "applied-success": "%1 스킨 적용 완료", + "revert-success": "기본 색으로 스킨 복구됨" } \ No newline at end of file diff --git a/public/language/ko/admin/appearance/themes.json b/public/language/ko/admin/appearance/themes.json index 3148a01337..0cfebc6430 100644 --- a/public/language/ko/admin/appearance/themes.json +++ b/public/language/ko/admin/appearance/themes.json @@ -1,11 +1,11 @@ { - "checking-for-installed": "Checking for installed themes...", - "homepage": "Homepage", - "select-theme": "Select Theme", - "current-theme": "Current Theme", - "no-themes": "No installed themes found", - "revert-confirm": "Are you sure you wish to restore the default NodeBB theme?", - "theme-changed": "Theme Changed", - "revert-success": "You have successfully reverted your NodeBB back to it's default theme.", - "restart-to-activate": "Please restart your NodeBB to fully activate this theme" + "checking-for-installed": "설치된 테마 확인 중...", + "homepage": "홈페이지", + "select-theme": "테마 선택", + "current-theme": "현재 테마", + "no-themes": "설치된 테마가 없습니다", + "revert-confirm": "정말 기본 NodeBB 테마로 복원하시겠습니까?", + "theme-changed": "테마 변경 완료", + "revert-success": "성공적으로 기본 NodeBB 테마로 복원됐습니다.", + "restart-to-activate": "변경된 테마를 완전히 활성화시키기 위해 NodeBB를 다시 시작해주십시오" } \ No newline at end of file diff --git a/public/language/ko/admin/development/info.json b/public/language/ko/admin/development/info.json index b2768ca212..cc6edaa69d 100644 --- a/public/language/ko/admin/development/info.json +++ b/public/language/ko/admin/development/info.json @@ -1,16 +1,18 @@ { - "you-are-on": "Info - You are on %1:%2", - "host": "host", + "you-are-on": "정보 - %1:%2에 있습니다.", + "nodes-responded": "%1 노드가 %2ms 내로 응답했습니다.", + "host": "호스트", "pid": "pid", "nodejs": "nodejs", - "online": "online", + "online": "온라인", "git": "git", - "load": "load", - "uptime": "uptime", + "memory": "메모리", + "load": "로드", + "uptime": "Uptime", - "registered": "Registered", + "registered": "등록됨", "sockets": "Sockets", - "guests": "Guests", + "guests": "미가입 사용자", - "info": "Info" + "info": "정보" } \ No newline at end of file diff --git a/public/language/ko/admin/development/logger.json b/public/language/ko/admin/development/logger.json index 6ab9558149..9a2a443810 100644 --- a/public/language/ko/admin/development/logger.json +++ b/public/language/ko/admin/development/logger.json @@ -1,12 +1,12 @@ { - "logger-settings": "Logger Settings", - "description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.", - "explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.", - "enable-http": "Enable HTTP logging", - "enable-socket": "Enable socket.io event logging", - "file-path": "Path to log file", - "file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal", + "logger-settings": "로거 설정", + "description": "체크 박스를 활성화하면, 터미널에서 로그를 볼 수 있게 됩니다. 만약 파일 경로를 지정하면, 로그가 지정한 파일에 대신 저장됩니다. HTTP 기록은 누가, 언제, 무엇을 포럼에서 했는지에 대한 통계를 내는 데 유용합니다. HTTP 리퀘스트들을 기록할 뿐 아니라, socket.io 이벤트들도 기록할 수 있습니다. Socket.io 기록은 redis-cli 모니터와 함께 사용하면 NodeBB의 내부 사항을 모니터하는 데 아주 유용할 수 있습니다.", + "explanation": "원하실 때 로깅 환경을 활성화/비활성화 하십시오. 재시작 할 필요는 없습니다.", + "enable-http": "HTTP 로깅 허용", + "enable-socket": "socket.io 이벤트 로깅 허용", + "file-path": "로그 파일 경로", + "file-path-placeholder": "/path/to/log/file.log ::: 터미널에서 로그를 보시려면 빈칸으로 두세요", - "control-panel": "Logger Control Panel", - "update-settings": "Update Logger Settings" + "control-panel": "로거 제어판", + "update-settings": "로거 설정 업데이트" } \ No newline at end of file diff --git a/public/language/ko/admin/extend/plugins.json b/public/language/ko/admin/extend/plugins.json index 1661a987b7..a669c7e2eb 100644 --- a/public/language/ko/admin/extend/plugins.json +++ b/public/language/ko/admin/extend/plugins.json @@ -1,47 +1,47 @@ { - "installed": "Installed", - "active": "Active", - "inactive": "Inactive", - "out-of-date": "Out of Date", - "none-found": "No plugins found.", - "none-active": "No Active Plugins", - "find-plugins": "Find Plugins", + "installed": "설치됨", + "active": "사용 중", + "inactive": "사용하지 않는 중", + "out-of-date": "업데이트가 안된", + "none-found": "플러그인을 찾을 수 없습니다.", + "none-active": "사용 중인 플러그인이 없습니다.", + "find-plugins": "플러그인 찾기", - "plugin-search": "Plugin Search", - "plugin-search-placeholder": "Search for plugin...", - "reorder-plugins": "Re-order Plugins", - "order-active": "Order Active Plugins", - "dev-interested": "Interested in writing plugins for NodeBB?", - "docs-info": "Full documentation regarding plugin authoring can be found in the NodeBB Docs Portal.", + "plugin-search": "플러그인 검색", + "plugin-search-placeholder": "플러그인을 검색하세요...", + "reorder-plugins": "플러그인 작동 순서 재배열", + "order-active": "사용 중인 플러그인 작동 순서 배열", + "dev-interested": "NodeBB 플러그인을 만드는 데 관심이 있으십니까?", + "docs-info": "플러그인 제작 관련 문서는 NodeBB Docs Portal에서 찾아보실 수 있습니다.", - "order.description": "Certain plugins work ideally when they are initialised before/after other plugins.", - "order.explanation": "Plugins load in the order specified here, from top to bottom", + "order.description": "특정 플러그인은 다른 플러그인의 초기화 전/후에 가장 이상적으로 작동합니다.", + "order.explanation": "플러그인들은 여기에 나열된 순서로 로드됩니다.", - "plugin-item.themes": "Themes", - "plugin-item.deactivate": "Deactivate", - "plugin-item.activate": "Activate", - "plugin-item.install": "Install", - "plugin-item.uninstall": "Uninstall", - "plugin-item.settings": "Settings", - "plugin-item.installed": "Installed", - "plugin-item.latest": "Latest", - "plugin-item.upgrade": "Upgrade", - "plugin-item.more-info": "For more information:", - "plugin-item.unknown": "Unknown", - "plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.", + "plugin-item.themes": "테마", + "plugin-item.deactivate": "비활성화", + "plugin-item.activate": "활성화", + "plugin-item.install": "설치", + "plugin-item.uninstall": "제거", + "plugin-item.settings": "설정", + "plugin-item.installed": "설치됨", + "plugin-item.latest": "최신", + "plugin-item.upgrade": "업그레이드", + "plugin-item.more-info": "추가적인 정보를 원하시면:", + "plugin-item.unknown": "알려지지 않은", + "plugin-item.unknown-explanation": "이 플러그인의 상태를 알 수 없습니다. 환경 설정에서 발생한 오류 때문일 수 있습니다.", - "alert.enabled": "Plugin Enabled", - "alert.disabled": "Plugin Disabled", - "alert.upgraded": "Plugin Upgraded", - "alert.installed": "Plugin Installed", - "alert.uninstalled": "Plugin Uninstalled", - "alert.activate-success": "Please restart your NodeBB to fully activate this plugin", - "alert.deactivate-success": "Plugin successfully deactivated", - "alert.upgrade-success": "Please reload your NodeBB to fully upgrade this plugin", - "alert.install-success": "Plugin successfully installed, please activate the plugin.", - "alert.uninstall-success": "The plugin has been successfully deactivated and uninstalled.", - "alert.suggest-error": "

NodeBB could not reach the package manager, proceed with installation of latest version?

Server returned (%1): %2
", - "alert.package-manager-unreachable": "

NodeBB could not reach the package manager, an upgrade is not suggested at this time.

", - "alert.incompatible": "

Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.

", - "alert.possibly-incompatible": "

No Compatibility Information Found

This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.

In the event that NodeBB cannot boot properly:

$ ./nodebb reset plugin=\"%1\"

Continue installation of latest version of this plugin?

" + "alert.enabled": "플러그인이 활성화됐습니다", + "alert.disabled": "플러그인이 비활성화됐습니다.", + "alert.upgraded": "플러그인이 업그레이드됐습니다.", + "alert.installed": "플러그인이 설치됐습니다.", + "alert.uninstalled": "플러그인이 제거됐습니다.", + "alert.activate-success": "이 플러그인을 완전히 활성화하려면 NodeBB를 다시 시작해주십시오.", + "alert.deactivate-success": "플러그인이 성공적으로 비활성화됐습니다.", + "alert.upgrade-success": "이 플러그인을 완전히 업그레이드 시키려면 NodeBB를 다시 로드해주십시오.", + "alert.install-success": "플러그인이 성공적으로 설치됐습니다. 플러그인을 활성화 해주십시오.", + "alert.uninstall-success": "플러그인이 성공적으로 비활성화되고 삭제됐습니다.", + "alert.suggest-error": "

NodeBB가 페키지 매니저 접근에 실패하였습니다. 최신 버젼을 설치하시겠습니까?

서버의 응답 (%1):%2
", + "alert.package-manager-unreachable": "

NodeBB가 페키지 매니저 접근에 실패했습니다. 지금 업그레이드 하는 것을 추천하지 않습니다.

", + "alert.incompatible": "

지금 사용하시는 NodeBB 버젼(v%1)에서는 이 플러그인을 v%2 버젼까지만 업그레이드 할 수 있습니다. 이 플러그인의 최신 버젼을 설치하고 싶으시다면 먼저 NodeBB를 업그레이드 해주십시오.

", + "alert.possibly-incompatible": "

호환성 관련 정보를 찾지 못했습니다.

이 플러그인은 현재 사용 중이신 NodeBB 버젼에 적합한 버젼을 명시하지 않았습니다. 따라서 완전한 호환성을 보장할 수 없고, 결과적으로 지금 사용중이신 NodeBB에 오류를 일으킬 수도 있습니다.

만약 NodeBB가 제대로 시작되지 않는다면:

$ ./nodebb reset plugin=\"%1\"

이 플러그인의 최신 버젼을 설치를 계속 하시겠습니까?

" } diff --git a/public/language/ko/admin/extend/rewards.json b/public/language/ko/admin/extend/rewards.json index 5383a90b33..8262d98597 100644 --- a/public/language/ko/admin/extend/rewards.json +++ b/public/language/ko/admin/extend/rewards.json @@ -1,17 +1,17 @@ { - "rewards": "Rewards", - "condition-if-users": "If User's", - "condition-is": "Is:", - "condition-then": "Then:", - "max-claims": "Amount of times reward is claimable", - "zero-infinite": "Enter 0 for infinite", - "delete": "Delete", - "enable": "Enable", - "disable": "Disable", - "control-panel": "Rewards Control", - "new-reward": "New Reward", + "rewards": "보상", + "condition-if-users": "만약 사용자의", + "condition-is": "다음의 조건을 충족한다면:", + "condition-then": "다음과 같은 행동을 취합니다:", + "max-claims": "보상을 받을 수 있는 횟수", + "zero-infinite": "무제한으로 설정하려면 0 으로 설정하세요", + "delete": "삭제", + "enable": "활성화", + "disable": "비활성화", + "control-panel": "보상 제어판", + "new-reward": "새로운 보상", - "alert.delete-success": "Successfully deleted reward", - "alert.no-inputs-found": "Illegal reward - no inputs found!", - "alert.save-success": "Successfully saved rewards" + "alert.delete-success": "성공적으로 보상을 삭제했습니다.", + "alert.no-inputs-found": "잘못된 보상 - 인풋이 없습니다!", + "alert.save-success": "성공적으로 보상을 저장했습니다." } \ No newline at end of file diff --git a/public/language/ko/admin/extend/widgets.json b/public/language/ko/admin/extend/widgets.json index 477bb15e56..135d0bc43b 100644 --- a/public/language/ko/admin/extend/widgets.json +++ b/public/language/ko/admin/extend/widgets.json @@ -1,19 +1,19 @@ { - "available": "Available Widgets", - "explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.", - "none-installed": "No widgets found! Activate the essential widgets plugin in the plugins control panel.", - "containers.available": "Available Containers", - "containers.explanation": "Drag and drop on top of any active widget", - "containers.none": "None", + "available": "사용 가능 위젯", + "explanation": "드롭다운 메뉴에서 위젯을 고르고 왼쪽에 있는 템플렛의 위젯 위치로 드래그하여 옮기십시오.", + "none-installed": "위젯을 찾을 수 없습니다! 필수 위젯 플러그인들을 플러그인 제어판에서 활성화하세요.", + "containers.available": "사용 가능한 컨테이너", + "containers.explanation": "아무 활성화된 위젯 위로 드래그해서 옮겨주십시오.", + "containers.none": "없음", "container.well": "Well", "container.jumbotron": "Jumbotron", - "container.panel": "Panel", - "container.panel-header": "Panel Header", - "container.panel-body": "Panel Body", - "container.alert": "Alert", + "container.panel": "제어판", + "container.panel-header": "제어판 헤더", + "container.panel-body": "제어판 몸통", + "container.alert": "경고", - "alert.confirm-delete": "Are you sure you wish to delete this widget?", - "alert.updated": "Widgets Updated", - "alert.update-success": "Successfully updated widgets" + "alert.confirm-delete": "정말 이 위젯을 삭제하시겠습니까?", + "alert.updated": "위젯 업데이트 됨", + "alert.update-success": "성공적으로 업데이트 된 위젯" } \ No newline at end of file diff --git a/public/language/ko/admin/general/dashboard.json b/public/language/ko/admin/general/dashboard.json index 61f4421fb5..b608a99113 100644 --- a/public/language/ko/admin/general/dashboard.json +++ b/public/language/ko/admin/general/dashboard.json @@ -1,69 +1,69 @@ { - "forum-traffic": "Forum Traffic", - "page-views": "Page Views", - "unique-visitors": "Unique Visitors", - "users": "Users", - "posts": "Posts", - "topics": "Topics", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", + "forum-traffic": "포럼 트래픽", + "page-views": "페이지 뷰", + "unique-visitors": "순 방문자수", + "users": "사용자", + "posts": "포스트", + "topics": "게시물", + "page-views-seven": "지난 7일간", + "page-views-thirty": "지난 30일간", + "page-views-last-day": "지난 24시간 동안", + "page-views-custom": "사용자 설정 날짜 기간", + "page-views-custom-start": "기간 시작", + "page-views-custom-end": "기간 끝", + "page-views-custom-help": "페이지 뷰를 확인하고 싶은 기간을 입력하세요. 만약 데이트 피커를 사용할 수 없다면, YYYY-MM-DD 포맷으로 입력해주세요.", + "page-views-custom-error": "유효한 기간을 다음과 같은 포맷으로 입력하세요 YYYY-MM-DD", - "stats.day": "Day", - "stats.week": "Week", - "stats.month": "Month", - "stats.all": "All Time", + "stats.day": "날", + "stats.week": "주", + "stats.month": "월", + "stats.all": "항상", - "updates": "Updates", - "running-version": "You are running NodeBB v%1.", - "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", + "updates": "업데이트", + "running-version": "NodeBB v%1 를 사용 중입니다.", + "keep-updated": "사용하시는 NodeBB의 보안 및 오류 업데이트를 항상 최신 버젼으로 유지하십시오.", + "up-to-date": "

최신 버전입니다

", + "upgrade-available": "

새로운 버전 (v%1) 이 출시 되었습니다. 사용하시는 NodeBB의 업데이트를 고려해보세요.

", + "prerelease-upgrade-available": "

옛 버전의 NodeBB를 사용하고 계십니다. 새로운 버전의(v%1) NodeBB가 출시되었으니 업데이트 하시는 것을 추천드립니다.

", + "prerelease-warning": "

이것은 정식 발표 전 버젼의 NodeBB 입니다. 예상치 못한 버그가 발생할 수 있습니다.

", + "running-in-development": "포럼이 개발자 모드로 실행되고 있습니다. 잠재적 취약점에 노출되어 있을 수 있으니 시스템 관리자에게 문의하십시오.", - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", - "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", + "notices": "알림", + "restart-not-required": "재시작 필요 없음", + "restart-required": "재시작 필요", + "search-plugin-installed": "설치된 플러그인 검색", + "search-plugin-not-installed": "설치되지 않은 플러그인 검색", + "search-plugin-tooltip": "검색 기능을 활성화하시려면 플러그인 페이지에서 검색 플러그인을 설치하세요.", - "control-panel": "System Control", - "reload": "Reload", - "restart": "Restart", - "restart-warning": "Reloading or Restarting your NodeBB will drop all existing connections for a few seconds.", - "maintenance-mode": "Maintenance Mode", - "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", - "realtime-chart-updates": "Realtime Chart Updates", + "control-panel": "시스템 제어", + "reload": "리로드", + "restart": "재시작", + "restart-warning": "NodeBB 앱을 리로드하거나 재시작할 경우에는 기존의 연결이 몇 초 동안 끊어집니다.", + "maintenance-mode": "점검 모드", + "maintenance-mode-title": "NodeBB 점검 모드를 설정하시려면 이곳을 클릭하세요.", + "realtime-chart-updates": "실시간 차트 업데이트", - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", + "active-users": "활동 중인 사용자", + "active-users.users": "사용자", + "active-users.guests": "게스트", + "active-users.total": "총", + "active-users.connections": "연결", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", + "anonymous-registered-users": "익명 vs 가입한 사용자", + "anonymous": "익명", + "registered": "가입한", - "user-presence": "User Presence", - "on-categories": "On categories list", - "reading-posts": "Reading posts", - "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", + "user-presence": "사용자 활동", + "on-categories": "카테고리 목록 보는 중", + "reading-posts": "게시물 읽기", + "browsing-topics": "토픽 보기", + "recent": "최근", + "unread": "읽지 않은", - "high-presence-topics": "High Presence Topics", + "high-presence-topics": "활동량이 많은 토픽", - "graphs.page-views": "Page Views", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users" + "graphs.page-views": "페이지 뷰", + "graphs.unique-visitors": "고유 방문자", + "graphs.registered-users": "등록된 사용자", + "graphs.anonymous-users": "익명의 사용자" } diff --git a/public/language/ko/admin/general/homepage.json b/public/language/ko/admin/general/homepage.json index 4866b8baf6..13d43e4de7 100644 --- a/public/language/ko/admin/general/homepage.json +++ b/public/language/ko/admin/general/homepage.json @@ -1,7 +1,7 @@ { - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", - "home-page-route": "Home Page Route", - "custom-route": "Custom Route", - "allow-user-home-pages": "Allow User Home Pages" + "home-page": "홈페이지", + "description": "사용자가 루트 URL에 들어갔을 때 어떤 페이지가 보일지 선택하세요.", + "home-page-route": "홈페이지 경로", + "custom-route": "사용자 지정 경로", + "allow-user-home-pages": "사용자가 직접 홈페이지를 설정할 수 있게 허용" } \ No newline at end of file diff --git a/public/language/ko/admin/general/languages.json b/public/language/ko/admin/general/languages.json index da45cade2c..2464b7b75a 100644 --- a/public/language/ko/admin/general/languages.json +++ b/public/language/ko/admin/general/languages.json @@ -1,5 +1,6 @@ { - "language-settings": "Language Settings", - "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "language-settings": "언어 설정", + "description": "기본 언어 설정은 사이트를 방문하는 모든 사용자들에게 적용됩니다.
하지만 사용자들이 직접 본인의 계정 설정 페이지에서 언어 설정을 바꿀 수 있습니다.", + "default-language": "기본 언어", + "auto-detect": "사용자의 언어 설정 자동으로 감지합니다." } \ No newline at end of file diff --git a/public/language/ko/admin/general/navigation.json b/public/language/ko/admin/general/navigation.json index c4ba0d09ac..6bef01eae2 100644 --- a/public/language/ko/admin/general/navigation.json +++ b/public/language/ko/admin/general/navigation.json @@ -1,27 +1,27 @@ { - "icon": "Icon:", - "change-icon": "change", - "route": "Route:", - "tooltip": "Tooltip:", - "text": "Text:", - "text-class": "Text Class: optional", - "id": "ID: optional", + "icon": "아이콘:", + "change-icon": "변경", + "route": "경로:", + "tooltip": "툴팁:", + "text": "텍스트:", + "text-class": "텍스트 클래스: 선택 사항", + "id": "ID: 선택 사항", - "properties": "Properties:", - "only-admins": "Only display to Admins", - "only-global-mods-and-admins": "Only display to Global Moderators and Admins", - "only-logged-in": "Only display to logged in users", - "open-new-window": "Open in a new window", + "properties": "속성:", + "only-admins": "관리자에게만 보이기", + "only-global-mods-and-admins": "(준)관리자와 관리자에게만 보이기", + "only-logged-in": "로그인한 사용자에게만 보이기", + "open-new-window": "새 창에서 열기", - "installed-plugins-required": "Installed Plugins Required:", - "search-plugin": "Search plugin", + "installed-plugins-required": "설치된 플러그인 필수:", + "search-plugin": "검색 플러그인", - "btn.delete": "Delete", - "btn.disable": "Disable", - "btn.enable": "Enable", + "btn.delete": "삭제", + "btn.disable": "비활성화", + "btn.enable": "활성화", - "available-menu-items": "Available Menu Items", - "custom-route": "Custom Route", - "core": "core", - "plugin": "plugin" + "available-menu-items": "이용 가능한 메뉴 항목", + "custom-route": "사용자 지정 경로", + "core": "핵심", + "plugin": "플러그인" } \ No newline at end of file diff --git a/public/language/ko/admin/general/social.json b/public/language/ko/admin/general/social.json index 23aedfcfaa..35c38ef758 100644 --- a/public/language/ko/admin/general/social.json +++ b/public/language/ko/admin/general/social.json @@ -1,5 +1,5 @@ { - "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" + "post-sharing": "포스트 공유", + "info-plugins-additional": "플러그인을 이용해서 포스트를 공유할 수 있는 네트워크를 추가할 수 있습니다.", + "save-success": "포스트 공유 네트워크 추가 완료!" } \ No newline at end of file diff --git a/public/language/ko/admin/general/sounds.json b/public/language/ko/admin/general/sounds.json index 95ccbde0f1..70cbb40922 100644 --- a/public/language/ko/admin/general/sounds.json +++ b/public/language/ko/admin/general/sounds.json @@ -1,9 +1,9 @@ { - "notifications": "Notifications", - "chat-messages": "Chat Messages", - "play-sound": "Play", - "incoming-message": "Incoming Message", - "outgoing-message": "Outgoing Message", - "upload-new-sound": "Upload New Sound", - "saved": "Settings Saved" + "notifications": "알림", + "chat-messages": "채팅 메세지", + "play-sound": "재생", + "incoming-message": "수신 메시지", + "outgoing-message": "발신 메시지", + "upload-new-sound": "새로운 사운드 업로드", + "saved": "설정 저장됨" } \ No newline at end of file diff --git a/public/language/ko/admin/manage/categories.json b/public/language/ko/admin/manage/categories.json index 7e2a5ce12e..f5971d9b3e 100644 --- a/public/language/ko/admin/manage/categories.json +++ b/public/language/ko/admin/manage/categories.json @@ -1,68 +1,69 @@ { - "settings": "Category Settings", - "privileges": "Privileges", + "settings": "게시판 설정", + "privileges": "권한", - "name": "Category Name", - "description": "Category Description", - "bg-color": "Background Colour", - "text-color": "Text Colour", - "bg-image-size": "Background Image Size", - "custom-class": "Custom Class", - "num-recent-replies": "# of Recent Replies", - "ext-link": "External Link", - "upload-image": "Upload Image", - "delete-image": "Remove", - "category-image": "Category Image", - "parent-category": "Parent Category", - "optional-parent-category": "(Optional) Parent Category", - "parent-category-none": "(None)", - "copy-settings": "Copy Settings From", - "optional-clone-settings": "(Optional) Clone Settings From Category", - "purge": "Purge Category", + "name": "게시판 이름", + "description": "게시판 설명", + "bg-color": "배경 색", + "text-color": "글자 색", + "bg-image-size": "배경 이미지 크기", + "custom-class": "사용자 정의 클래스", + "num-recent-replies": "최근 답장 갯수", + "ext-link": "외부 링크", + "is-section": "이 게시판을 섹션으로 취급", + "upload-image": "이미지 업로드", + "delete-image": "삭제", + "category-image": "게시판 이미지", + "parent-category": "상위 카테고리", + "optional-parent-category": "(선택) 상위 게시판", + "parent-category-none": "(없음)", + "copy-settings": "에서 설정 복사", + "optional-clone-settings": "(선택) 게시판에서 설정 복사", + "purge": "게시판 삭제", - "enable": "Enable", - "disable": "Disable", - "edit": "Edit", + "enable": "활성화", + "disable": "비활성화", + "edit": "편집", - "select-category": "Select Category", - "set-parent-category": "Set Parent Category", + "select-category": "게시판 선택", + "set-parent-category": "상위 게시판 설정", - "privileges.description": "You can configure the access control privileges for this category in this section. Privileges can be granted on a per-user or a per-group basis. You can add a new user to this table by searching for them in the form below.", - "privileges.warning": "Note: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.", - "privileges.section-viewing": "Viewing Privileges", - "privileges.section-posting": "Posting Privileges", - "privileges.section-moderation": "Moderation Privileges", - "privileges.section-user": "User", - "privileges.search-user": "Add User", - "privileges.no-users": "No user-specific privileges in this category.", - "privileges.section-group": "Group", - "privileges.group-private": "This group is private", - "privileges.search-group": "Add Group", - "privileges.copy-to-children": "Copy to Children", - "privileges.copy-from-category": "Copy from Category", - "privileges.inherit": "If the registered-users group is granted a specific privilege, all other groups receive an implicit privilege, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the registered-users user group, and so, privileges for additional groups need not be explicitly granted.", + "privileges.description": "이 섹션에서 게시판에 대한 접근 권한을 설정할 수 있습니다. 권한은 사용자 별 또는 그룹 별로 부여할 수 있습니다. 아래 양식으로 검색하여 새로운 사용자를 테이블에 추가할 수 있습니다.", + "privileges.warning": "참고: 권한 설정은 즉시 적용됩니다. 설정을 변경한 후 게시판을 따로 저장할 필요가 없습니다.", + "privileges.section-viewing": "권한 보기", + "privileges.section-posting": "게시물 작성 권한", + "privileges.section-moderation": "(준)관리자 권한", + "privileges.section-user": "사용자", + "privileges.search-user": "사용자 추가", + "privileges.no-users": "이 게시판에는 사용자 별 권한이 없습니다.", + "privileges.section-group": "그룹", + "privileges.group-private": "이 그룹은 비공개입니다", + "privileges.search-group": "그룹 추가", + "privileges.copy-to-children": "하위로 복사", + "privileges.copy-from-category": "게시판에서 복사", + "privileges.inherit": "만약 registered-users그룹이 특정 권한을 허가 받는다면 모든 다른 그룹들 또한 따로 추가하거나 체크하지 않더라도 암시적 권한을 얻게 됩니다. 모든 유저가 registered-users 그룹의 멤버이기 때문에 다른 추가적인 그룹에 대한 권한은 따로 허가 받을 필요가 없습니다.", - "analytics.back": "Back to Categories List", - "analytics.title": "Analytics for \"%1\" category", - "analytics.pageviews-hourly": "Figure 1 – Hourly page views for this category", - "analytics.pageviews-daily": "Figure 2 – Daily page views for this category", - "analytics.topics-daily": "Figure 3 – Daily topics created in this category", - "analytics.posts-daily": "Figure 4 – Daily posts made in this category", + "analytics.back": "게시판 목록으로 돌아가기", + "analytics.title": "\"%1\" 게시판 분석 결과", + "analytics.pageviews-hourly": "Figure 1 – 이 게시판에 대한 시간 당 페이지 뷰 수", + "analytics.pageviews-daily": "Figure 2 – 이 게시판에 대한 일일 페이지 뷰 수", + "analytics.topics-daily": "Figure 3 – 오늘 이 게시판에 생성된 게시물들", + "analytics.posts-daily": "Figure 4 – 오늘 이 게시판에 생성된 포스트들", - "alert.created": "Created", - "alert.create-success": "Category successfully created!", - "alert.none-active": "You have no active categories.", - "alert.create": "Create a Category", - "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", - "alert.confirm-purge": "

Do you really want to purge this category \"%1\"?

Warning! All topics and posts in this category will be purged!

Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category temporarily, you'll want to \"disable\" the category instead.

", - "alert.purge-success": "Category purged!", - "alert.copy-success": "Settings Copied!", - "alert.set-parent-category": "Set Parent Category", - "alert.updated": "Updated Categories", - "alert.updated-success": "Category IDs %1 successfully updated.", - "alert.upload-image": "Upload category image", - "alert.find-user": "Find a User", - "alert.user-search": "Search for a user here...", - "alert.find-group": "Find a Group", - "alert.group-search": "Search for a group here..." + "alert.created": "생성됨", + "alert.create-success": "게시판 성공적으로 생성됨!", + "alert.none-active": "활성화된 게시판이 없습니다.", + "alert.create": "게시판 생성", + "alert.confirm-moderate": "이 사용자 그룹에 (준)관리지 권한을 부여하시겠습니까? 이 그룹은 공개 그룹이므로 모든 사용자가 자유롭게 가입 할 수 있습니다.", + "alert.confirm-purge": "

정말로 이 게시판을 \"%1\" 제거하시겠습니까?

경고!이 게시판에 속한 모든 게시물과 포스트가 삭제됩니다!

게시판을 제거하게 되면 모든 게시물과 포스트가 삭제되고 데이터베이스에서도 이 게시판이 삭제됩니다. 만약 일시적으로 게시판을 없애고 싶으시다면 삭제 대신 \"비활성화\"를 해주십시오.

", + "alert.purge-success": "게시판 제거됨!", + "alert.copy-success": "설정 복사됨!", + "alert.set-parent-category": "상위 게시판 설정", + "alert.updated": "업데이트 된 게시판", + "alert.updated-success": "게시판 ID %1 성공적으로 업데이트 됨.", + "alert.upload-image": "게시판 이미지 업로드", + "alert.find-user": "사용자 검색", + "alert.user-search": "여기서 사용자를 검색하십시오...", + "alert.find-group": "그룹 검색", + "alert.group-search": "여기서 그룹을 검색하십시오..." } \ No newline at end of file diff --git a/public/language/ko/admin/manage/groups.json b/public/language/ko/admin/manage/groups.json index c019ec9823..4ee5c2839a 100644 --- a/public/language/ko/admin/manage/groups.json +++ b/public/language/ko/admin/manage/groups.json @@ -1,35 +1,35 @@ { - "name": "Group Name", - "description": "Group Description", - "member-count": "Member Count", - "system": "System Group", - "edit": "Edit", - "search-placeholder": "Search", - "create": "Create Group", - "description-placeholder": "A short description about your group", - "create-button": "Create", + "name": "그룹 이름", + "description": "그룹 설명", + "member-count": "멤버 수", + "system": "시스템 그룹", + "edit": "수정", + "search-placeholder": "검색", + "create": "그룹 만들기", + "description-placeholder": "그룹에 대한 짧은 설명", + "create-button": "만들기", - "alerts.create-failure": "Uh-Oh

There was a problem creating your group. Please try again later!

", - "alerts.confirm-delete": "Are you sure you wish to delete this group?", + "alerts.create-failure": "이런

그룹을 만드는 데 문제가 발생했습니다. 잠시 후 다시 시도해주세요!

", + "alerts.confirm-delete": "이 그룹을 삭제하시겠습니까?", - "edit.name": "Name", - "edit.description": "Description", - "edit.user-title": "Title of Members", - "edit.icon": "Group Icon", - "edit.label-color": "Group Label Color", - "edit.show-badge": "Show Badge", - "edit.private-details": "If enabled, joining of groups requires approval from a group owner.", - "edit.private-override": "Warning: Private groups is disabled at system level, which overrides this option.", - "edit.disable-requests": "Disable join requests", - "edit.hidden": "Hidden", - "edit.hidden-details": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "edit.add-user": "Add User to Group", - "edit.add-user-search": "Search Users", - "edit.members": "Member List", - "control-panel": "Groups Control Panel", - "revert": "Revert", + "edit.name": "이름", + "edit.description": "설명", + "edit.user-title": "사용자 타이틀", + "edit.icon": "그룹 아이콘", + "edit.label-color": "그룹 라벨 색깔", + "edit.show-badge": "뱃지 보여주기", + "edit.private-details": "만약 활성화된다면, 그룹에 가입하기 위해서는 그룹 운영자의 승인이 필요합니다.", + "edit.private-override": "경고: 비공개 그룹은 시스템에 의해 비활성화 되었으며, 시스템 설정은 이 옵션보다 우위를 가집니다.", + "edit.disable-requests": "가입 신청 비활성화", + "edit.hidden": "숨김", + "edit.hidden-details": "활성 시 그룹 목록에 노출되지 않습니다. 또한 구성원은 초대를 통해서만 가입이 가능합니다.", + "edit.add-user": "그룹에 사용자 추가", + "edit.add-user-search": "사용자 검색", + "edit.members": "멤버 목록", + "control-panel": "그룹 제어판", + "revert": "되돌리기", - "edit.no-users-found": "No Users Found", - "edit.confirm-remove-user": "Are you sure you want to remove this user?", - "edit.save-success": "Changes saved!" + "edit.no-users-found": "사용자를 찾을 수 없습니다", + "edit.confirm-remove-user": "이 사용자를 제거하시겠습니까?", + "edit.save-success": "변경 사항을 저장했습니다!" } \ No newline at end of file diff --git a/public/language/ko/admin/manage/ip-blacklist.json b/public/language/ko/admin/manage/ip-blacklist.json index 5106434351..7a137dc9ad 100644 --- a/public/language/ko/admin/manage/ip-blacklist.json +++ b/public/language/ko/admin/manage/ip-blacklist.json @@ -1,15 +1,15 @@ { - "lead": "Configure your IP blacklist here.", - "description": "Occasionally, a user account ban is not enough of a deterrant. Other times, restricting access to the forum to a specific IP or a range of IPs is the best way to protect a forum. In these scenarios, you can add troublesome IP addresses or entire CIDR blocks to this blacklist, and they will be prevented from logging in to or registering a new account.", - "active-rules": "Active Rules", - "validate": "Validate Blacklist", - "apply": "Apply Blacklist", - "hints": "Syntax Hints", - "hint-1": "Define a single IP addresses per line. You can add IP blocks as long as they follow the CIDR format (e.g. 192.168.100.0/22).", - "hint-2": "You can add in comments by starting lines with the # symbol.", + "lead": "여기에 IP 차단 목록을 구성하십시오.", + "description": "가끔씩 사용자 계정 차단만으로는 부족할 수 있습니다. 그럴 경우에는 특정 IP 또는 IP 범위로 접근을 차단하는 것이 포럼을 보호하는 가장 좋은 방법입니다. 문제가 되는 IP 주소들이나 CIDR 블록 전체를 블랙리스트에 추가하면 로그인하거나 새 계정을 등록할 수 없습니다.", + "active-rules": "사용 중인 룰", + "validate": "블랙리스트 확인", + "apply": "블랙리스트 적용", + "hints": "구문 힌트", + "hint-1": "각 줄마다 IP 주소 하나를 정의하십시오. IP 블록을 CIDR 형식 (예: 192.168.100.0/22)을 따르는 한 추가 할 수 있습니다. ", + "hint-2": "#기호로 행을 시작하여 주석을 추가 할 수 있습니다.", - "validate.x-valid": "%1 out of %2 rule(s) valid.", - "validate.x-invalid": "The following %1 rules are invalid:", + "validate.x-valid": "%2 룰(들) 중 %1 유효함.", + "validate.x-invalid": "다음 %1 규칙들은 유효하지 않습니다:", - "alerts.applied-success": "Blacklist Applied" + "alerts.applied-success": "블랙리스트 적용됨" } \ No newline at end of file diff --git a/public/language/ko/admin/manage/registration.json b/public/language/ko/admin/manage/registration.json index f51b4d56e6..b96c52c1ac 100644 --- a/public/language/ko/admin/manage/registration.json +++ b/public/language/ko/admin/manage/registration.json @@ -1,20 +1,20 @@ { - "queue": "Queue", - "description": "There are no users in the registration queue.
To enable this feature, go to Settings → User → User Registration and set Registration Type to \"Admin Approval\".", + "queue": "큐", + "description": "가입 대기 큐에 사용자가 없습니다.
이 기능을 사용하려면, 설정 → 사용자 → 사용자 가입으로 가서, 가입 종류를 \"관리자 승인\"으로 바꾸세요.", - "list.name": "Name", - "list.email": "Email", - "list.ip": "IP", - "list.time": "Time", - "list.username-spam": "Frequency: %1 Appears: %2 Confidence: %3", - "list.email-spam": "Frequency: %1 Appears: %2", - "list.ip-spam": "Frequency: %1 Appears: %2", + "list.name": "이름", + "list.email": "이메일", + "list.ip": "IP 주소", + "list.time": "시간", + "list.username-spam": "빈도: %1 출연 유무: %2 신뢰도: %3", + "list.email-spam": "빈도: %1 출연 유무: %2", + "list.ip-spam": "빈도: %1 출연 유무: %2", - "invitations": "Invitations", - "invitations.description": "Below is a complete list of invitations sent. Use ctrl-f to search through the list by email or username.

The username will be displayed to the right of the emails for users who have redeemed their invitations.", - "invitations.inviter-username": "Inviter Username", - "invitations.invitee-email": "Invitee Email", - "invitations.invitee-username": "Invitee Username (if registered)", + "invitations": "초대", + "invitations.description": "발송된 초대의 목록을 아래에서 보실 수 있습니다. ctrl-f을 이용해서 이메일이나 사용자명으로 목록을 검색하세요.

초대에 응답한 사용자들은 이메일 옆에 사용자명이 표시됩니다.", + "invitations.inviter-username": "초대 발송인 사용자 이름", + "invitations.invitee-email": "초대 수신인 이메일", + "invitations.invitee-username": "초대 수신인 사용자 이름 (가입 시)", - "invitations.confirm-delete": "Are you sure you wish to delete this invitation?" + "invitations.confirm-delete": "이 초대를 삭제하시겠습니까?" } \ No newline at end of file diff --git a/public/language/ko/admin/manage/tags.json b/public/language/ko/admin/manage/tags.json index db40e9f098..979a5a8af2 100644 --- a/public/language/ko/admin/manage/tags.json +++ b/public/language/ko/admin/manage/tags.json @@ -1,18 +1,18 @@ { - "none": "Your forum does not have any topics with tags yet.", - "bg-color": "Background Colour", - "text-color": "Text Colour", - "create-modify": "Create & Modify Tags", - "description": "Select tags via clicking and/or dragging, use shift to select multiple.", - "create": "Create Tag", - "modify": "Modify Tags", - "delete": "Delete Selected Tags", - "search": "Search for tags...", - "settings": "Click here to visit the tag settings page.", - "name": "Tag Name", + "none": "현재 포럼에는 태그가 달린 주제가 없습니다.", + "bg-color": "배경색", + "text-color": "텍스트 색", + "create-modify": "태그 생성/수정", + "description": "태그를 클릭하거나 드래그해서 선택하고, Shift 를 눌러 다중 선택하세요.", + "create": "태그 생성", + "modify": "태그 수정", + "delete": "선택된 태그 삭제", + "search": "태그 검색", + "settings": "태그 설정 페이지를 방문하시려면 클릭하세요", + "name": "태그 이름", - "alerts.editing-multiple": "Editing multiple tags", - "alerts.editing-x": "Editing \"%1\" tag", - "alerts.confirm-delete": "Do you want to delete the selected tags?", - "alerts.update-success": "Tag Updated!" + "alerts.editing-multiple": "다수의 태그 수정 중", + "alerts.editing-x": "\"%1\" 태그 수정 중", + "alerts.confirm-delete": "선택된 태그들을 삭제하시겠습니까?", + "alerts.update-success": "태그가 업데이트 됐습니다! " } \ No newline at end of file diff --git a/public/language/ko/admin/manage/users.json b/public/language/ko/admin/manage/users.json index f1651a814b..a1db2decf6 100644 --- a/public/language/ko/admin/manage/users.json +++ b/public/language/ko/admin/manage/users.json @@ -1,91 +1,91 @@ { - "users": "Users", - "edit": "Edit", - "make-admin": "Make Admin", - "remove-admin": "Remove Admin", - "validate-email": "Validate Email", - "send-validation-email": "Send Validation Email", - "password-reset-email": "Send Password Reset Email", - "ban": "Ban User(s)", - "temp-ban": "Ban User(s) Temporarily", - "unban": "Unban User(s)", - "reset-lockout": "Reset Lockout", - "reset-flags": "Reset Flags", - "delete": "Delete User(s)", - "purge": "Delete User(s) and Content", - "download-csv": "Download CSV", - "invite": "Invite", - "new": "New User", + "users": "유저", + "edit": "편집", + "make-admin": "관리자로 만들기", + "remove-admin": "관리자 제거", + "validate-email": "이메일 인증", + "send-validation-email": "인증 이메일 보내기", + "password-reset-email": "패스워드 리셋 이메일 보내기", + "ban": "사용자 차단", + "temp-ban": "일시적으로 사용자 차단", + "unban": "사용자 차단 해제", + "reset-lockout": "락아웃 리셋", + "reset-flags": "신고 리셋", + "delete": "사용자 삭제", + "purge": "사용자와 사용자의 컨텐츠 삭제", + "download-csv": "CSV 다운로드", + "invite": "초대", + "new": "새로운 사용자", - "pills.latest": "Latest Users", - "pills.unvalidated": "Not Validated", - "pills.no-posts": "No Posts", - "pills.top-posters": "Top Posters", - "pills.top-rep": "Most Reputation", - "pills.inactive": "Inactive", - "pills.flagged": "Most Flagged", - "pills.banned": "Banned", - "pills.search": "User Search", + "pills.latest": "최근 사용자", + "pills.unvalidated": "인증되지 않음", + "pills.no-posts": "글 없음", + "pills.top-posters": "가장 많은 글을 올린 사용자", + "pills.top-rep": "가장 등급이 높은", + "pills.inactive": "휴면", + "pills.flagged": "가장 신고가 많은", + "pills.banned": "차단됨", + "pills.search": "사용자 검색", - "search.username": "By User Name", - "search.username-placeholder": "Enter a username to search", - "search.email": "By Email", - "search.email-placeholder": "Enter a email to search", - "search.ip": "By IP Address", - "search.ip-placeholder": "Enter an IP Address to search", - "search.not-found": "User not found!", + "search.username": "이름으로", + "search.username-placeholder": "검색할 사용자명을 입력하세요", + "search.email": "이메일로", + "search.email-placeholder": "검색할 이메일을 입력하세요", + "search.ip": "IP 주소로", + "search.ip-placeholder": "검색할 IP 주소를 입력하세요", + "search.not-found": "사용자를 찾을 수 없습니다!", - "inactive.3-months": "3 months", - "inactive.6-months": "6 months", - "inactive.12-months": "12 months", + "inactive.3-months": "3개월", + "inactive.6-months": "6개월", + "inactive.12-months": "12개월", "users.uid": "uid", - "users.username": "username", - "users.email": "email", - "users.postcount": "postcount", - "users.reputation": "reputation", - "users.flags": "flags", - "users.joined": "joined", - "users.last-online": "last online", - "users.banned": "banned", + "users.username": "사용자명", + "users.email": "이메일", + "users.postcount": "글 개수", + "users.reputation": "등급", + "users.flags": "신고", + "users.joined": "가입됨", + "users.last-online": "마지막 로그인", + "users.banned": "차단됨", - "create.username": "User Name", - "create.email": "Email", - "create.email-placeholder": "Email of this user", - "create.password": "Password", - "create.password-confirm": "Confirm Password", + "create.username": "이름", + "create.email": "이메일", + "create.email-placeholder": "이 사용자의 이메일", + "create.password": "패스워드", + "create.password-confirm": "패스워드 재입력", - "temp-ban.length": "Ban Length", - "temp-ban.reason": "Reason (Optional)", - "temp-ban.hours": "Hours", - "temp-ban.days": "Days", - "temp-ban.explanation": "Enter the length of time for the ban. Note that a time of 0 will be a considered a permanent ban.", + "temp-ban.length": "차단 기간", + "temp-ban.reason": "이유 (선택 사항)", + "temp-ban.hours": "시간", + "temp-ban.days": "일", + "temp-ban.explanation": "차단할 기간을 입력하세요. 0을 입력하면 영구적인 차단으로 간주됩니다.", - "alerts.confirm-ban": "Do you really want to ban this user permanently?", - "alerts.confirm-ban-multi": "Do you really want to ban these users permanently?", - "alerts.ban-success": "User(s) banned!", - "alerts.button-ban-x": "Ban %1 user(s)", - "alerts.unban-success": "User(s) unbanned!", - "alerts.lockout-reset-success": "Lockout(s) reset!", - "alerts.flag-reset-success": "Flags(s) reset!", - "alerts.no-remove-yourself-admin": "You can't remove yourself as Administrator!", - "alerts.make-admin-success": "User(s) are now administrators.", - "alerts.confirm-remove-admin": "Do you really want to remove admins?", - "alerts.remove-admin-success": "User(s) are no longer administrators.", - "alerts.confirm-validate-email": "Do you want to validate email(s) of these user(s)?", - "alerts.validate-email-success": "Emails validated", - "alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?", - "alerts.confirm-delete": "Warning!
Do you really want to delete user(s)?
This action is not reversable! Only the user account will be deleted, their posts and topics will remain.", - "alerts.delete-success": "User(s) Deleted!", - "alerts.confirm-purge": "Warning!
Do you really want to delete user(s) and their content?
This action is not reversable! All user data and content will be erased!", - "alerts.create": "Create User", - "alerts.button-create": "Create", - "alerts.button-cancel": "Cancel", - "alerts.error-passwords-different": "Passwords must match!", - "alerts.error-x": "Error

%1

", - "alerts.create-success": "User created!", + "alerts.confirm-ban": "정말 이 사용자를 영구적으로 차단하시겠습니까?", + "alerts.confirm-ban-multi": "정말 이 사용자들을 영구적으로 차단하시겠습니까?", + "alerts.ban-success": "사용자(들)이 차단됐습니다!", + "alerts.button-ban-x": "%1 명의 사용자를 차단", + "alerts.unban-success": "사용자의 차단이 해제됐습니다!", + "alerts.lockout-reset-success": "락아웃이 리셋됐습니다!", + "alerts.flag-reset-success": "신고가 리셋됐습니다!", + "alerts.no-remove-yourself-admin": "관리자이기 때문에 본인을 삭제할 수 없습니다!", + "alerts.make-admin-success": "이 사용자는 이제 관리자입니다.", + "alerts.confirm-remove-admin": "정말 이 관리자를 제거하시겠습니까?", + "alerts.remove-admin-success": "이 사용자는 이제 더 이상 관리자가 아닙니다.", + "alerts.confirm-validate-email": "이 사용자(들)의 이메일을 인증하시겠습니까?", + "alerts.validate-email-success": "이메일 인증됨", + "alerts.password-reset-confirm": "이 사용자(들)에게 패스워드 리셋 이메일을 보내시겠습니까?", + "alerts.confirm-delete": "경고!
정말 이 사용자를 삭제하시겠습니까?
이 조치는 돌이킬 수 없습니다! 사용자의 계정만 삭제되고, 사용자가 작성한 게시물이나 포스트는 남아있게 됩니다.", + "alerts.delete-success": "사용자가 삭제됐습니다!", + "alerts.confirm-purge": "경고!
정말 이 사용자와 사용자의 컨텐츠를 삭제하시겠습니까?
이 조치는 돌이킬 수 없습니다! 사용자의 모든 데이터와 컨텐츠가 삭제됩니다!", + "alerts.create": "사용자 생성", + "alerts.button-create": "만들기", + "alerts.button-cancel": "취소", + "alerts.error-passwords-different": "비밀번호가 일치하지 않습니다!", + "alerts.error-x": "에러

%1

", + "alerts.create-success": "사용자가 생성됐습니다!", - "alerts.prompt-email": "Email: ", - "alerts.email-sent-to": "An invitation email has been sent to %1", - "alerts.x-users-found": "%1 user(s) found! Search took %2 ms." + "alerts.prompt-email": "이메일:", + "alerts.email-sent-to": "%1에게 초대 이메일이 발송됐습니다.", + "alerts.x-users-found": "%1명의 사용자를 찾았습니다! 검색에 %2 ms가 소요됐습니다." } \ No newline at end of file diff --git a/public/language/ko/admin/menu.json b/public/language/ko/admin/menu.json index 985c540e8a..1c47a6c8df 100644 --- a/public/language/ko/admin/menu.json +++ b/public/language/ko/admin/menu.json @@ -1,74 +1,74 @@ { - "section-general": "General", - "general/dashboard": "Dashboard", - "general/homepage": "Home Page", - "general/navigation": "Navigation", - "general/languages": "Languages", - "general/sounds": "Sounds", - "general/social": "Social", + "section-general": "일반", + "general/dashboard": "대시보드", + "general/homepage": "홈페이지", + "general/navigation": "네비게이션", + "general/languages": "언어", + "general/sounds": "소리", + "general/social": "SNS", - "section-manage": "Manage", - "manage/categories": "Categories", - "manage/tags": "Tags", - "manage/users": "Users", - "manage/registration": "Registration Queue", - "manage/groups": "Groups", - "manage/ip-blacklist": "IP Blacklist", + "section-manage": "관리", + "manage/categories": "게시판", + "manage/tags": "태그", + "manage/users": "용자", + "manage/registration": "회원 가입 승인 대기자", + "manage/groups": "그룹", + "manage/ip-blacklist": "IP 블랙리스트", - "section-settings": "Settings", - "settings/general": "General", - "settings/reputation": "Reputation", - "settings/email": "Email", - "settings/user": "User", - "settings/group": "Group", - "settings/guest": "Guests", - "settings/uploads": "Uploads", - "settings/post": "Post", - "settings/chat": "Chat", - "settings/pagination": "Pagination", - "settings/tags": "Tags", - "settings/notifications": "Notifications", - "settings/cookies": "Cookies", - "settings/web-crawler": "Web Crawler", - "settings/sockets": "Sockets", - "settings/advanced": "Advanced", + "section-settings": "설정", + "settings/general": "일반", + "settings/reputation": "등급", + "settings/email": "이메일", + "settings/user": "사용자", + "settings/group": "룹", + "settings/guest": "미가입 사용자", + "settings/uploads": "로드", + "settings/post": "글", + "settings/chat": "채팅", + "settings/pagination": "페이지 매기기", + "settings/tags": "태그", + "settings/notifications": "알림", + "settings/cookies": "쿠키", + "settings/web-crawler": "웹 크롤러", + "settings/sockets": "소켓", + "settings/advanced": "고급", - "settings.page-title": "%1 Settings", + "settings.page-title": "%1 설정", - "section-appearance": "Appearance", - "appearance/themes": "Themes", - "appearance/skins": "Skins", - "appearance/customise": "Custom HTML & CSS", + "section-appearance": "스타일", + "appearance/themes": "테마", + "appearance/skins": "스킨", + "appearance/customise": "사용자 정의 HTML & CSS", - "section-extend": "Extend", - "extend/plugins": "Plugins", - "extend/widgets": "Widgets", - "extend/rewards": "Rewards", + "section-extend": "추가 기능", + "extend/plugins": "플러그인", + "extend/widgets": "위젯", + "extend/rewards": "보상", - "section-social-auth": "Social Authentication", + "section-social-auth": "SNS로 로그인", - "section-plugins": "Plugins", - "extend/plugins.install": "Install Plugins", + "section-plugins": "플러그인", + "extend/plugins.install": "플러그인 설치", - "section-advanced": "Advanced", - "advanced/database": "Database", - "advanced/events": "Events", - "advanced/logs": "Logs", - "advanced/errors": "Errors", - "advanced/cache": "Cache", - "development/logger": "Logger", - "development/info": "Info", + "section-advanced": "고급", + "advanced/database": "데이터베이스", + "advanced/events": "이벤트", + "advanced/logs": "로그", + "advanced/errors": "에러", + "advanced/cache": "캐시", + "development/logger": "로거", + "development/info": "정보", - "reload-forum": "Reload Forum", - "restart-forum": "Restart Forum", - "logout": "Log out", - "view-forum": "View Forum", + "reload-forum": "포럼 리로드", + "restart-forum": "포럼 재시작", + "logout": "로그아웃", + "view-forum": "포럼 보기", - "search.placeholder": "Search...", - "search.no-results": "No results...", - "search.search-forum": "Search the forum for ", - "search.keep-typing": "Type more to see results...", - "search.start-typing": "Start typing to see results...", + "search.placeholder": "검색...", + "search.no-results": "검색 결과가 없습니다...", + "search.search-forum": "포럼에서 를 검색하세요", + "search.keep-typing": "검색 결과를 보기 위해 더 입력하세요...", + "search.start-typing": "검색 결과를 보기 위해 여기 입력하세요...", - "connection-lost": "Connection to %1 has been lost, attempting to reconnect..." + "connection-lost": "%1과의 연결이 끊어졌습니다. 다시 연결을 시도하는 중입니다..." } \ No newline at end of file diff --git a/public/language/ko/admin/settings/advanced.json b/public/language/ko/admin/settings/advanced.json index 05a1929cf0..b9617e8882 100644 --- a/public/language/ko/admin/settings/advanced.json +++ b/public/language/ko/admin/settings/advanced.json @@ -1,19 +1,19 @@ { - "maintenance-mode": "Maintenance Mode", - "maintenance-mode.help": "When the forum is in maintenance mode, all requests will be redirected to a static holding page. Administrators are exempt from this redirection, and are able to access the site normally.", - "maintenance-mode.message": "Maintenance Message", - "headers": "Headers", - "headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame", - "headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB", + "maintenance-mode": "점검 모드", + "maintenance-mode.help": "포럼이 점검 모드일 경우 모든 요청은 정적 페이지로 라다이렉트될 것입니다. 관리자는 이 리다이렉션의 예외가 되고 사이트에 접속하는 것이 가능합니다.", + "maintenance-mode.message": "점검 메세지", + "headers": "헤더", + "headers.allow-from": "NodeBB를 iFrame에 삽입할 수 있게 하시려면, ALLOW-FROM(NodeBB Embedding할 수 있는 도메인)을 설정하세요", + "headers.powered-by": "NodeBB 의 \"Powered By\" 를 커스터마이징하기", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty", + "headers.acao-help": "모든 사이트에 접근을 거부하시려면, 빈칸으로 남기세요", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", - "traffic-management": "Traffic Management", - "traffic.help": "NodeBB deploys equipped with a module that automatically denies requests in high-traffic situations. You can tune these settings here, although the defaults are a good starting point.", - "traffic.enable": "Enable Traffic Management", - "traffic.event-lag": "Event Loop Lag Threshold (in milliseconds)", - "traffic.event-lag-help": "Lowering this value decreases wait times for page loads, but will also show the \"excessive load\" message to more users. (Restart required)", - "traffic.lag-check-interval": "Check Interval (in milliseconds)", - "traffic.lag-check-interval-help": "Lowering this value causes NodeBB to become more sensitive to spikes in load, but may also cause the check to become too sensitive. (Restart required)" + "traffic-management": "트래픽 관리", + "traffic.help": "NodeBB는 트래픽이 많은 상황에서는 자동으로 리퀘스트를 거부하는 모듈을 가지고 있습니다. 이에 관련된 세팅은 디폴트 값들도 괜찮지만, 여기서 세부 조율도 가능합니다.", + "traffic.enable": "트래픽 관리 허용", + "traffic.event-lag": "이벤트 루프 간격(단위: 1/1000초)", + "traffic.event-lag-help": "이 값을 낮추게 되면 페이지 로딩에 걸리는 시간이 단축되지만, 더 많은 사용자들이 \"과도한 로딩\"이라는 메시지를 보게됩니다. (재시작 필요)", + "traffic.lag-check-interval": "트래픽 체크 간격(단위: 1/1000초)", + "traffic.lag-check-interval-help": "이 값을 낮추게 되면 갑작스런 로딩값 변화에 더 민감해지지만, 과하게 예민한 반응을 야기할 수 있습니다. (재시작 필요)" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/chat.json b/public/language/ko/admin/settings/chat.json index 0b22127341..a629cede01 100644 --- a/public/language/ko/admin/settings/chat.json +++ b/public/language/ko/admin/settings/chat.json @@ -1,9 +1,9 @@ { - "chat-settings": "Chat Settings", - "disable": "Disable chat", - "disable-editing": "Disable chat message editing/deletion", - "disable-editing-help": "Administrators and global moderators are exempt from this restriction", - "max-length": "Maximum length of chat messages", - "max-room-size": "Maximum number of users in chat rooms", - "delay": "Time between chat messages in milliseconds" + "chat-settings": "채팅 설정", + "disable": "채팅 비활성화", + "disable-editing": "채팅 메세지 수정/삭제 비활성화", + "disable-editing-help": "관리자는 이 제한에서 면제됩니다.", + "max-length": "채팅 메세지의 최대 길이", + "max-room-size": "채팅방의 최대 인원 수", + "delay": "채팅 메세지 간 시간 간격(단위: 1/1000초)" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/cookies.json b/public/language/ko/admin/settings/cookies.json index f8b0f0538b..2922aae4e6 100644 --- a/public/language/ko/admin/settings/cookies.json +++ b/public/language/ko/admin/settings/cookies.json @@ -1,11 +1,11 @@ { - "eu-consent": "EU Consent", - "consent.enabled": "Enabled", - "consent.message": "Notification message", - "consent.acceptance": "Acceptance message", - "consent.link-text": "Policy Link Text", - "consent.blank-localised-default": "Leave blank to use NodeBB localised defaults", - "settings": "Settings", - "cookie-domain": "Session cookie domain", - "blank-default": "Leave blank for default" + "eu-consent": "EU 법률 관련", + "consent.enabled": "활성화됨", + "consent.message": "알림 메세지", + "consent.acceptance": "허가 메세지", + "consent.link-text": "약관 조항 링크에 표시할 텍스트", + "consent.blank-localised-default": "NodeBB 의 번역을 사용하시려면 빈칸으로 두세요", + "settings": "설정", + "cookie-domain": "세션 쿠키 도메인", + "blank-default": "기본값을 사용하시려면 빈칸으로 두세요" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/email.json b/public/language/ko/admin/settings/email.json index 1e92c88490..c7f2f910a4 100644 --- a/public/language/ko/admin/settings/email.json +++ b/public/language/ko/admin/settings/email.json @@ -1,25 +1,25 @@ { - "email-settings": "Email Settings", - "address": "Email Address", - "address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.", - "from": "From Name", - "from-help": "The from name to display in the email.", - "gmail-routing": "Gmail Routing", - "gmail-routing-help1": "There have been reports of Gmail Routing not working on accounts with heightened security. In those scenarios, you will have to configure your GMail account to allow less secure apps.", - "gmail-routing-help2": "For more information about this workaround, please consult this NodeMailer article on the issue. An alternative would be to utilise a third-party emailer plugin such as SendGrid, Mailgun, etc. Browse available plugins here.", - "gmail-transport": "Route emails through a Gmail/Google Apps account", - "gmail-transport.username": "Username", - "gmail-transport.username-help": "Enter the full email address here, especially if you are using a Google Apps managed domain.", - "gmail-transport.password": "Password", - "template": "Edit Email Template", - "template.select": "Select Email Template", - "template.revert": "Revert to Original", - "testing": "Email Testing", - "testing.select": "Select Email Template", - "testing.send": "Send Test Email", - "testing.send-help": "The test email will be sent to the currently logged in user's email address.", - "subscriptions": "Email Subscriptions", - "subscriptions.disable": "Disable subscriber notification emails", - "subscriptions.hour": "Digest Hour", - "subscriptions.hour-help": "Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
The approximate server time is:
The next daily digest is scheduled to be sent " + "email-settings": "이메일 설정", + "address": "이메일 주소", + "address-help": "아래 이메일 주소는 수신인의 \"보낸 사람\"과 \"답장하기\" 항목에서 보이게 됩니다.", + "from": "보낸 사람 이름", + "from-help": "이메일에 표시할 보낸 사람 이름", + "gmail-routing": "Gmail 라우팅", + "gmail-routing-help1": "보안 설정이 강화된 Google 계정의 경우에는 Gmail 라우팅이 작동하지 않는다는 보고가 있었습니다. 그러한 경우에는, Gmail에서 보안 수준이 낮은 앱 허용을 사용해야 합니다.", + "gmail-routing-help2": "이 방법에 대한 더 자세한 정보는 이 문제에 관한 NodeMailer 기사를 참조 해주세요. 다른 대안으로는 SendGrid, Mailgun 등의 제 3의 이메일 플러그인을 사용하는 것이 있습니다. 여기서 가능한 플러그인을 검색해보세요.", + "gmail-transport": " 지메일/구글 계정을 통해 이메일을 라우팅", + "gmail-transport.username": "사용자명", + "gmail-transport.username-help": "구글 앱스와 연동된 도메인을 사용하신다면 특히, 이메일주소를 생략없이 적어주세요", + "gmail-transport.password": "패스워드", + "template": "이메일 템플릿 수정", + "template.select": "이메일 템플릿 선택", + "template.revert": "수정 취소", + "testing": "이메일 테스팅", + "testing.select": "이메일 템플릿 선택", + "testing.send": "테스트 이메일 보내기", + "testing.send-help": "현재 로그인 중인 사용자의 이메일로 테스트 이메일을 보냅니다.", + "subscriptions": "이메일 구독", + "subscriptions.disable": "구독자 알림 메일 비활성화", + "subscriptions.hour": "발송 시간", + "subscriptions.hour-help": "정기 이메일을 보낼 시간을 기입해 주세요(예: 0은 자정, 17은 오후 5시 입니다. 이 시간은 서버기준이며, 귀하의 시스템 시간과 일치하지 않을 수 있습니다.
서버 시간은 입니다.
다음 정기 이메일은 에 발송 예정입니다." } \ No newline at end of file diff --git a/public/language/ko/admin/settings/general.json b/public/language/ko/admin/settings/general.json index 3f2814bd88..3d6b8b6364 100644 --- a/public/language/ko/admin/settings/general.json +++ b/public/language/ko/admin/settings/general.json @@ -1,32 +1,32 @@ { - "site-settings": "Site Settings", - "title": "Site Title", - "title.name": "Your Community Name", - "title.show-in-header": "Show Site Title in Header", - "browser-title": "Browser Title", - "browser-title-help": "If no browser title is specified, the site title will be used", - "title-layout": "Title Layout", - "title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}", - "description.placeholder": "A short description about your community", - "description": "Site Description", - "keywords": "Site Keywords", - "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo": "Site Logo", - "logo.image": "Image", - "logo.image-placeholder": "Path to a logo to display on forum header", - "logo.upload": "Upload", + "site-settings": "사이트 관리", + "title": "사이트 제목", + "title.name": "커뮤니티 이름", + "title.show-in-header": "헤더에 사이트 제목 표시", + "browser-title": "브라우저 타이틀", + "browser-title-help": "브라우저 타이틀이 입력되지 않으면 사이트 타이틀이 사용될 것입니다", + "title-layout": "타이틀 레이아웃", + "title-layout-help": "브라우저 타이틀이 어떻게 표기 될지 설정해 주세요. 예: {pageTitle} | {browserTitle} ", + "description.placeholder": "커뮤니티에 대한 간략한 설명", + "description": "사이트 설명", + "keywords": "사이트 키워드", + "keywords-placeholder": "콤마(,)로 분리된 커뮤니티를 묘사하는 키워드들", + "logo": "사이트 로고", + "logo.image": "사진", + "logo.image-placeholder": "로고 파일 저장 위치", + "logo.upload": "업로드", "logo.url": "URL", - "logo.url-placeholder": "The URL of the site logo", - "logo.url-help": "When the logo is clicked, send users to this address. If left blank, user will be sent to the forum index.", - "logo.alt-text": "Alt Text", - "log.alt-text-placeholder": "Alternative text for accessibility", - "favicon": "Favicon", - "favicon.upload": "Upload", - "touch-icon": "Homescreen/Touch Icon", - "touch-icon.upload": "Upload", - "touch-icon.help": "Recommended size and format: 192x192, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", - "outgoing-links": "Outgoing Links", - "outgoing-links.warning-page": "Use Outgoing Links Warning Page", - "search-default-sort-by": "Search default sort by", - "outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page" + "logo.url-placeholder": "사이트 로고 URL", + "logo.url-help": "사용자가 로고를 클릭했을 때 연결할 URL 주소. 빈칸으로 남겨두면 사이트 홈페이지로 연결됩니다.", + "logo.alt-text": "대체 텍스트", + "log.alt-text-placeholder": "대체할 글", + "favicon": "Favicon(브라우저 탭에 표기되는 이미지)", + "favicon.upload": "업로드", + "touch-icon": "홈스크린 아이콘", + "touch-icon.upload": "업로드", + "touch-icon.help": "추천 사이즈: 192 x 192(픽셀), PNG 확장자만 가능. 설정되지 않은 경우, favicon을 사용합니다.", + "outgoing-links": "외부 링크", + "outgoing-links.warning-page": "외부 링크 경고페이지 사용", + "search-default-sort-by": "검색결과 정열기준 기본값", + "outgoing-links.whitelist": "경고창이 필요없는 외부 링크 도메인(whitelist)" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/group.json b/public/language/ko/admin/settings/group.json index 1ae88c9cf5..75a4e8efe1 100644 --- a/public/language/ko/admin/settings/group.json +++ b/public/language/ko/admin/settings/group.json @@ -1,12 +1,12 @@ { - "general": "General", - "private-groups": "Private Groups", - "private-groups.help": "If enabled, joining of groups requires the approval of the group owner (Default: enabled)", - "private-groups.warning": "Beware! If this option is disabled and you have private groups, they automatically become public.", - "allow-creation": "Allow Group Creation", - "allow-creation-help": "If enabled, users can create groups (Default: disabled)", - "max-name-length": "Maximum Group Name Length", - "cover-image": "Group Cover Image", - "default-cover": "Default Cover Images", - "default-cover-help": "Add comma-separated default cover images for groups that don't have an uploaded cover image" + "general": "일반", + "private-groups": "비공개 그룹", + "private-groups.help": " 활성화 되어있다면 그룹에 가입하는 것은 그룹 관리자의 허가를 필요로 합니다. (기본 설정: 활성화)", + "private-groups.warning": "주의 이 옵션이 비활성화 돼있고 당신에게 비공개 그룹이 있다면 그 그룹들은 모두 공개로 전환될 것입니다.", + "allow-creation": "그룹 생성 허용", + "allow-creation-help": "활성화 돼있다면 유저가 그룹을 생성할 수 있습니다. (기본: 비활성화)", + "max-name-length": "그룹 명 최대 길이", + "cover-image": "그룹 커버 사진", + "default-cover": "기본 커버 사진", + "default-cover-help": "기본 커버 사진 목록을 콤마(,) 로 구분지어 입력해 주세요. " } \ No newline at end of file diff --git a/public/language/ko/admin/settings/guest.json b/public/language/ko/admin/settings/guest.json index 6b2ac2c8b2..bc0d920595 100644 --- a/public/language/ko/admin/settings/guest.json +++ b/public/language/ko/admin/settings/guest.json @@ -1,8 +1,8 @@ { - "handles": "Guest Handles", - "handles.enabled": "Allow guest handles", - "handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\"", - "privileges": "Guest Privileges", - "privileges.can-search": "Allow guests to search without logging in", - "privileges.can-search-users": "Allow guests to search users without logging in" + "handles": "게스트 처리", + "handles.enabled": "미가입 사용자 닉네임 설정 허가", + "handles.enabled-help": "이 옵션은 게스트들이 포스트를 작성할 때 이름을 적는 공간을 제공합니다. 이 옵션이 비활성화 돼있다면 \"Guest\" 라고 표시될 것입니다.", + "privileges": "게스트 권한", + "privileges.can-search": "로그인 없이 검색 허용", + "privileges.can-search-users": "로그인 없이 유저 검색 허용" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/notifications.json b/public/language/ko/admin/settings/notifications.json index 4eff7f341a..0b2bab42c5 100644 --- a/public/language/ko/admin/settings/notifications.json +++ b/public/language/ko/admin/settings/notifications.json @@ -1,5 +1,5 @@ { - "notifications": "Notifications", - "welcome-notification": "Welcome Notification", - "welcome-notification-link": "Welcome Notification Link" + "notifications": "알림", + "welcome-notification": "환영 알림", + "welcome-notification-link": "환영 알림 링크" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/pagination.json b/public/language/ko/admin/settings/pagination.json index 27d71b4de5..19ae2f326c 100644 --- a/public/language/ko/admin/settings/pagination.json +++ b/public/language/ko/admin/settings/pagination.json @@ -1,9 +1,9 @@ { - "pagination": "Pagination Settings", - "enable": "Paginate topics and posts instead of using infinite scroll.", - "topics": "Topic Pagination", - "posts-per-page": "Posts per Page", - "categories": "Category Pagination", - "topics-per-page": "Topics per Page", - "initial-num-load": "Initial Number of Topics to Load on Unread, Recent, and Popular" + "pagination": "페이지 설정", + "enable": "무한 스크롤 대신 페이지로 주제와 포스트 보여주기", + "topics": "주제 페이지", + "posts-per-page": "페이지 당 포스트", + "categories": "카테고리 페이지", + "topics-per-page": "페이지 당 주제 수", + "initial-num-load": "읽지 않은 글, 최근 작성 글, 인기 글 게시판에서 처음 보여줄 게시글 개수" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/post.json b/public/language/ko/admin/settings/post.json index aca8b39d64..ce9c3bfe72 100644 --- a/public/language/ko/admin/settings/post.json +++ b/public/language/ko/admin/settings/post.json @@ -1,46 +1,46 @@ { - "sorting": "Post Sorting", - "sorting.post-default": "Default Post Sorting", - "sorting.oldest-to-newest": "Oldest to Newest", - "sorting.newest-to-oldest": "Newest to Oldest", - "sorting.most-votes": "Most Votes", - "sorting.topic-default": "Default Topic Sorting", - "restrictions": "Posting Restrictions", - "restrictions.seconds-between": "Seconds between Posts", - "restrictions.seconds-between-new": "Seconds between Posts for New Users", - "restrictions.rep-threshold": "Reputation threshold before this restriction is lifted", - "restrictions.seconds-defore-new": "Seconds before new user can post", - "restrictions.seconds-edit-after": "Number of seconds users are allowed to edit posts after posting. (0 disabled)", - "restrictions.seconds-delete-after": "Number of seconds users are allowed to delete posts after posting. (0 disabled)", - "restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics. (0 disabled)", - "restrictions.min-title-length": "Minimum Title Length", - "restrictions.max-title-length": "Maximum Title Length", - "restrictions.min-post-length": "Minimum Post Length", - "restrictions.max-post-length": "Maximum Post Length", - "restrictions.days-until-stale": "Days until Topic is considered stale", - "restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic.", - "timestamp": "Timestamp", - "timestamp.cut-off": "Date cut-off (in days)", - "timestamp.cut-off-help": "Dates & times will be shown in a relative manner (e.g. \"3 hours ago\" / \"5 days ago\"), and localised into various\n\t\t\t\t\tlanguages. After a certain point, this text can be switched to display the localised date itself\n\t\t\t\t\t(e.g. 5 Nov 2016 15:30).
(Default: 30, or one month). Set to 0 to always display dates, leave blank to always display relative times.", - "teaser": "Teaser Post", - "teaser.last-post": "Last – Show the latest post, including the original post, if no replies", - "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", - "teaser.first": "First", - "unread": "Unread Settings", - "unread.cutoff": "Unread cutoff days", - "unread.min-track-last": "Minimum posts in topic before tracking last read", - "recent": "Recent Settings", - "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", - "signature": "Signature Settings", - "signature.disable": "Disable signatures", - "signature.no-links": "Disable links in signatures", - "signature.no-images": "Disable images in signatures", - "signature.max-length": "Maximum Signature Length", - "composer": "Composer Settings", - "composer-help": "The following settings govern the functionality and/or appearance of the post composer shown\n\t\t\t\tto users when they create new topics, or reply to existing topics.", - "composer.show-help": "Show \"Help\" tab", - "composer.enable-plugin-help": "Allow plugins to add content to the help tab", - "composer.custom-help": "Custom Help Text", - "ip-tracking": "IP Tracking", - "ip-tracking.each-post": "Track IP Address for each post" + "sorting": "포스트 정렬", + "sorting.post-default": "기본 포스트 정렬", + "sorting.oldest-to-newest": "오래된 순", + "sorting.newest-to-oldest": "최신 순", + "sorting.most-votes": "추천수 순으로 정렬", + "sorting.topic-default": "게시물 정렬기준 기본값", + "restrictions": "글 작성 제약사항", + "restrictions.seconds-between": "글 작성 간 시간(초)", + "restrictions.seconds-between-new": "신규 사용자인 경우, 글 작성 간 시간(초)", + "restrictions.rep-threshold": "위 제약을 해제하기 위한 최소 등급", + "restrictions.seconds-defore-new": "신규 사용자 글 작성 허가 전 대기시간", + "restrictions.seconds-edit-after": "사용자가 글 작성 후 편집 허가 전 대기시간", + "restrictions.seconds-delete-after": "사용자가 글 작성 후 삭제 허가 전 대기시간", + "restrictions.replies-no-delete": "게시글이 삭제 불가로 변환되기 위한 답글 수", + "restrictions.min-title-length": "최소 제목 길이", + "restrictions.max-title-length": "최대 제목 길이", + "restrictions.min-post-length": "최소 게시글 길이", + "restrictions.max-post-length": "최대 게시글 길이", + "restrictions.days-until-stale": "게시물 유효기간(일)", + "restrictions.stale-help": "게시글이 유효기간을 지나면, 해당 게시글에 답글 작성하는 사용자에게 경고 메세지 보내기.", + "timestamp": "시간표기", + "timestamp.cut-off": "상대시간 표기 기간(일)", + "timestamp.cut-off-help": "날짜 및 시간은 상대시간으로 표기 (예: \"3시간 전\" / \"5일 전\"). 표기 기간이 지나면 지역시간으로 변환(예: 2016년 11월 5일 15:30).
(기본값: 30일, 또는 한달). 0으로 지정 시 항상 날짜 표기, 비워둘 시 항상 상대시간 표기.", + "teaser": "미리보기", + "teaser.last-post": "최근 글 - 가장 최근 글 보여주기. 답글이 없을 시 게시물 본문 보여주기", + "teaser.last-reply": "최근 글 - 가장 최근 글 보여주기. 답글이 없을 시 \"답글 없음\" 표기", + "teaser.first": "첫 글 보여주기", + "unread": "읽지 않은 글 설정", + "unread.cutoff": "읽지 않은 글 표기 기간", + "unread.min-track-last": "마지막으로 읽은 글 추적 기능을 사용할 최소 글 수", + "recent": "최근 작성된 글 설정", + "recent.categoryFilter.disable": "최근 작성된 글 페이지에서 무시 중인 게시판의 게시물도 포함", + "signature": "서명 설정", + "signature.disable": "서명 비활성화", + "signature.no-links": "서명에 포함된 바로가기 비활성화", + "signature.no-images": "서명에 포함된 사진 비활성화", + "signature.max-length": "최대 서명 길이", + "composer": "글쓰기 창 설정", + "composer-help": "밑의 설정들은 사용자가 새로운 글을 작성하거나 답글을 작성 할 때 나타나는 글쓰기 창의 기능과 외형에 영향을 끼칩니다.", + "composer.show-help": "\"도움말\" 탭 표시", + "composer.enable-plugin-help": "플러그인이 도움말 탭에 내용 추가하는 것을 허용", + "composer.custom-help": "사용자 설정 \"도움말\" 내용", + "ip-tracking": "IP 추적", + "ip-tracking.each-post": "모든 글의 IP 주소 추적" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/reputation.json b/public/language/ko/admin/settings/reputation.json index f0e59e8db9..b6e5eea171 100644 --- a/public/language/ko/admin/settings/reputation.json +++ b/public/language/ko/admin/settings/reputation.json @@ -1,9 +1,9 @@ { - "reputation": "Reputation Settings", - "disable": "Disable Reputation System", - "disable-down-voting": "Disable Down Voting", - "votes-are-public": "All Votes Are Public", - "thresholds": "Activity Thresholds", - "min-rep-downvote": "Minimum reputation to downvote posts", - "min-rep-flag": "Minimum reputation to flag posts" + "reputation": "평판 설정", + "disable": "평판 시스템 비활성화", + "disable-down-voting": "비추천 비활성화", + "votes-are-public": "모든 투표는 공개적입니다.", + "thresholds": "포럼 활동 기준선", + "min-rep-downvote": "평판이 낮아 이 게시물을 다운보트할 수 없습니다.", + "min-rep-flag": "평판이 낮아 이 게시물을 신고할 수 없습니다." } \ No newline at end of file diff --git a/public/language/ko/admin/settings/sockets.json b/public/language/ko/admin/settings/sockets.json index d04ee42fcf..27aa3e130f 100644 --- a/public/language/ko/admin/settings/sockets.json +++ b/public/language/ko/admin/settings/sockets.json @@ -1,6 +1,6 @@ { - "reconnection": "Reconnection Settings", - "max-attempts": "Max Reconnection Attempts", - "default-placeholder": "Default: %1", - "delay": "Reconnection Delay" + "reconnection": "재접속 환경", + "max-attempts": "최대 재접속시도", + "default-placeholder": "기본: %1", + "delay": "재접속 지체" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/tags.json b/public/language/ko/admin/settings/tags.json index 6f31f60ba0..d07917070b 100644 --- a/public/language/ko/admin/settings/tags.json +++ b/public/language/ko/admin/settings/tags.json @@ -1,12 +1,12 @@ { - "tag": "Tag Settings", - "min-per-topic": "Minimum Tags per Topic", - "max-per-topic": "Maximum Tags per Topic", - "min-length": "Minimum Tag Length", - "max-length": "Maximum Tag Length", - "goto-manage": "Click here to visit the tag management page.", - "privacy": "Privacy", - "list-private": "Make the tags list private", - "related-topics": "Related Topics", - "max-related-topics": "Maximum related topics to display (if supported by theme)" + "tag": "태그 설정", + "min-per-topic": "주제별 최소 태그", + "max-per-topic": "주제별 최대 태그", + "min-length": "태그 최소 길이", + "max-length": "태그 최대 길이", + "goto-manage": "태그 관리 페이지를 방문하시려면 클릭하세요", + "privacy": "개인 정보 보호", + "list-private": "태그 리스트를 개인만 볼 수 있게 바꿉니다.", + "related-topics": "관련된 주제들", + "max-related-topics": "테마가 지원하는 보여질 관련된 주제들의 최대 개수" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/uploads.json b/public/language/ko/admin/settings/uploads.json index 35eaa5a58f..3ec4700711 100644 --- a/public/language/ko/admin/settings/uploads.json +++ b/public/language/ko/admin/settings/uploads.json @@ -1,28 +1,28 @@ { - "posts": "Posts", - "allow-files": "Allow users to upload regular files", - "private": "Make uploaded files private", - "max-image-width": "Resize images down to specified width (in pixels)", - "max-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)", - "max-file-size": "Maximum File Size (in KiB)", - "max-file-size-help": "(in kilobytes, default: 2048 KiB)", - "allow-topic-thumbnails": "Allow users to upload topic thumbnails", - "topic-thumb-size": "Topic Thumb Size", - "allowed-file-extensions": "Allowed File Extensions", - "allowed-file-extensions-help": "Enter comma-separated list of file extensions here (e.g. pdf,xls,doc). An empty list means all extensions are allowed.", - "profile-avatars": "Profile Avatars", - "allow-profile-image-uploads": "Allow users to upload profile images", - "convert-profile-image-png": "Convert profile image uploads to PNG", - "default-avatar": "Custom Default Avatar", - "upload": "Upload", - "profile-image-dimension": "Profile Image Dimension", - "profile-image-dimension-help": "(in pixels, default: 128 pixels)", - "max-profile-image-size": "Maximum Profile Image File Size", - "max-profile-image-size-help": "(in kilobytes, default: 256 KiB)", - "max-cover-image-size": "Maximum Cover Image File Size", - "max-cover-image-size-help": "(in kilobytes, default: 2,048 KiB)", - "keep-all-user-images": "Keep old versions of avatars and profile covers on the server", - "profile-covers": "Profile Covers", - "default-covers": "Default Cover Images", - "default-covers-help": "Add comma-separated default cover images for accounts that don't have an uploaded cover image" + "posts": "게시물", + "allow-files": "유저들이 보통 파일들을 업로드하는것을 허용", + "private": "업로드된 파일들을 개인만 볼 수 있게 바꿉니다.", + "max-image-width": "이미지를 특정 가로 길이까지 축소 (단위: 픽셀)", + "max-image-width-help": "(단위: 픽셀, 기본값: 760 픽셀, 비활성화를 원하시면 0으로 지정하세요)", + "max-file-size": "최대 파일 사이즈(KB)", + "max-file-size-help": "(단위: KB, 기본값: 2048KB)", + "allow-topic-thumbnails": "사용자들이 게시물 썸네일을 업로드 하는것을 허용", + "topic-thumb-size": "게시물 썸네일 사이즈", + "allowed-file-extensions": "사용가능한 파일 확장자", + "allowed-file-extensions-help": "파일 확장자 목록을 콤마(,) 로 구분지어 입력해주세요(예: pdf,xls,doc). 빈칸으로 남기면 모든 확장자를 허용합니다. ", + "profile-avatars": "프로필 사진", + "allow-profile-image-uploads": "사용자들이 프로필 사진 업로드 하는것을 허용", + "convert-profile-image-png": "업로드 된 프로필 사진 확장자를 PNG로 변환", + "default-avatar": "사용자 설정 기본 프로필 사진", + "upload": "업로드", + "profile-image-dimension": "프로필 사진 규격", + "profile-image-dimension-help": "(단위: 픽셀, 기본값: 128 픽셀)", + "max-profile-image-size": "프로필 사진 최대 크기", + "max-profile-image-size-help": "(단위: KB, 기본값: 256KB)", + "max-cover-image-size": "커버 사진 최대 크기", + "max-cover-image-size-help": "(단위: KB, 기본값: 2048KB)", + "keep-all-user-images": "이전 프로필 사진과 커버 사진 서버에 저장", + "profile-covers": "프로필 커버 사진", + "default-covers": "기본 커버 사진", + "default-covers-help": "기본 커버 사진 목록을 콤마(,) 로 구분지어 입력해 주세요. " } diff --git a/public/language/ko/admin/settings/user.json b/public/language/ko/admin/settings/user.json index b8f51c9288..0f670b8ead 100644 --- a/public/language/ko/admin/settings/user.json +++ b/public/language/ko/admin/settings/user.json @@ -1,60 +1,62 @@ { - "authentication": "Authentication", - "allow-local-login": "Allow local login", - "require-email-confirmation": "Require Email Confirmation", - "email-confirm-interval": "User may not resend a confirmation email until", - "email-confirm-email2": "minutes have elapsed", - "allow-login-with": "Allow login with", - "allow-login-with.username-email": "Username or Email", - "allow-login-with.username": "Username Only", - "allow-login-with.email": "Email Only", - "account-settings": "Account Settings", - "disable-username-changes": "Disable username changes", - "disable-email-changes": "Disable email changes", - "disable-password-changes": "Disable password changes", - "allow-account-deletion": "Allow account deletion", - "user-info-private": "Make user info private", - "themes": "Themes", - "disable-user-skins": "Prevent users from choosing a custom skin", - "account-protection": "Account Protection", - "login-attempts": "Login attempts per hour", - "login-attempts-help": "If login attempts to a user's account exceeds this threshold, that account will be locked for a pre-configured amount of time", - "lockout-duration": "Account Lockout Duration (minutes)", - "login-days": "Days to remember user login sessions", - "password-expiry-days": "Force password reset after a set number of days", - "registration": "User Registration", - "registration-type": "Registration Type", - "registration-type.normal": "Normal", - "registration-type.admin-approval": "Admin Approval", - "registration-type.admin-approval-ip": "Admin Approval for IPs", - "registration-type.invite-only": "Invite Only", - "registration-type.admin-invite-only": "Admin Invite Only", - "registration-type.disabled": "No registration", - "registration-type.help": "Normal - Users can register from the /register page.
\nAdmin Approval - User registrations are placed in an approval queue for administrators.
\nAdmin Approval for IPs - Normal for new users, Admin Approval for IP addresses that already have an account.
\nInvite Only - Users can invite others from the users page.
\nAdmin Invite Only - Only administrators can invite others from users and admin/manage/users pages.
\nNo registration - No user registration.
", - "registration.max-invites": "Maximum Invitations per User", - "max-invites": "Maximum Invitations per User", - "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", - "min-username-length": "Minimum Username Length", - "max-username-length": "Maximum Username Length", - "min-password-length": "Minimum Password Length", - "min-password-strength": "Minimum Password Strength", - "max-about-me-length": "Maximum About Me Length", - "terms-of-use": "Forum Terms of Use (Leave blank to disable)", - "user-search": "User Search", - "user-search-results-per-page": "Number of results to display", - "default-user-settings": "Default User Settings", - "show-email": "Show email", - "show-fullname": "Show fullname", - "restrict-chat": "Only allow chat messages from users I follow", - "outgoing-new-tab": "Open outgoing links in new tab", - "topic-search": "Enable In-Topic Searching", - "digest-freq": "Subscribe to Digest", - "digest-freq.off": "Off", - "digest-freq.daily": "Daily", - "digest-freq.weekly": "Weekly", - "digest-freq.monthly": "Monthly", - "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", - "email-post-notif": "Send an email when replies are made to topics I am subscribed to", - "follow-created-topics": "Follow topics you create", - "follow-replied-topics": "Follow topics that you reply to" + "authentication": "로그인", + "allow-local-login": "로컬 로그인 허용", + "require-email-confirmation": "이메일 인증 필수화", + "email-confirm-interval": "유저는 인증 이메일을 전송할 수 없습니다:", + "email-confirm-email2": "분 후", + "allow-login-with": "로그인 허용 수단", + "allow-login-with.username-email": "사용자명 또는 이메일", + "allow-login-with.username": "사용자명", + "allow-login-with.email": "이메일", + "account-settings": "계정 관리", + "disable-username-changes": "사용자명 변경 비활성화", + "disable-email-changes": "이메일 주소 변경 비활성화", + "disable-password-changes": "패스워드 변경 비활성화", + "allow-account-deletion": "계정 삭제 허용", + "user-info-private": "사용자 정보를 개인만 볼수 있게 바꿉니다.", + "themes": "테마", + "disable-user-skins": "일반 사용자가 스킨 지정 금지", + "account-protection": "계정 보호", + "login-attempts": "시간당 가능한 로그인 시도 횟수", + "login-attempts-help": "사용자의 로그인 시도가 이 횟수제한을 초과하면 정해진 시간만큼 해당 계정이 잠깁니다.", + "lockout-duration": "계정 잠금 기간 (분)", + "login-days": "사용자 로그인 세션 유지 기간 (일)", + "password-expiry-days": "주기적으로 패스워드 리셋", + "registration": "사용자 등록", + "registration-type": "등록 유형", + "registration-type.normal": "일반", + "registration-type.admin-approval": "관리자 승인", + "registration-type.admin-approval-ip": "관리자 IP 승인", + "registration-type.invite-only": "사용자 초대", + "registration-type.admin-invite-only": "관리자 초대", + "registration-type.disabled": "신규 가입 불가", + "registration-type.help": "일반 - 사용자가 /register 페이지에서 가입 가능
\n관리자 승인 - 관리자 승인 전까지 사용자의 가입신청이 가입 신청 대기줄 에 위치
\n관리자 IP 승인 - 새로운 사용자에게는 일반 유형과 동일. 이미 등록된 IP로 새로 가입할 시 관리자 승인 필요
\n사용자 초대 - 사용자가 사용자 목록 페이지에서 초대
\n관리자 초대 - 관리자 만이 사용자 목록 페이지 admin/manage/users 페이지에서 초대 가능
\n신규 가입 불가 - 신규 가입 없음
", + "registration.max-invites": "최대 가능한 초대 수", + "max-invites": "최대 가능한 초대 수", + "max-invites-help": "아무런 제한을 두지 않으려면 0. 관리자는 초대 수 에 제한이 없습니다.
\"사용자 초대\" 설정에서만 반영됩니다.", + "invite-expiration": "초대장 만료 기간 (일)", + "invite-expiration-help": "초대장 유효 일수", + "min-username-length": "사용자명 최소 길이", + "max-username-length": "사용자명 최대 길이", + "min-password-length": "패스워드 최소 길이", + "min-password-strength": "패스워드 최소 강도", + "max-about-me-length": "자기소개 최대 길이", + "terms-of-use": "이용약관(미입력 시 비활성화)", + "user-search": "사용자 검색", + "user-search-results-per-page": "표시할 결과 수", + "default-user-settings": "사용자 설정 기본값", + "show-email": "이메일 공개", + "show-fullname": "이름 공개", + "restrict-chat": "내가 팔로우하는 이용자로부터만 대화를 허용", + "outgoing-new-tab": "외부 링크를 새로운 탭에서 열람", + "topic-search": "토픽 내 검색 허용", + "digest-freq": "포럼 이메일 정기구독", + "digest-freq.off": "해제", + "digest-freq.daily": "매일", + "digest-freq.weekly": "매주", + "digest-freq.monthly": "매달", + "email-chat-notifs": "오프라인일 때 채팅 메시지가 도착하면 알림 메일 보내기", + "email-post-notif": "내가 구독한 주제에 답글이 달리면 메일 보내기", + "follow-created-topics": "작성한 게시물 팔로우", + "follow-replied-topics": "답글 단 게시물을 팔로우" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/web-crawler.json b/public/language/ko/admin/settings/web-crawler.json index 2e0d31d12b..cfebc7ca05 100644 --- a/public/language/ko/admin/settings/web-crawler.json +++ b/public/language/ko/admin/settings/web-crawler.json @@ -1,10 +1,10 @@ { - "crawlability-settings": "Crawlability Settings", - "robots-txt": "Custom Robots.txt Leave blank for default", - "sitemap-feed-settings": "Sitemap & Feed Settings", - "disable-rss-feeds": "Disable RSS Feeds", - "disable-sitemap-xml": "Disable Sitemap.xml", - "sitemap-topics": "Number of Topics to display in the Sitemap", - "clear-sitemap-cache": "Clear Sitemap Cache", - "view-sitemap": "View Sitemap" + "crawlability-settings": "크롤링 설정", + "robots-txt": "사용자 지정 Robots.txt 기본값을 쓰시려면 비워두세요", + "sitemap-feed-settings": "사이트맵 & 피드 관리", + "disable-rss-feeds": "RSS Feeds 비활성화", + "disable-sitemap-xml": "Sitemap.xml 비활성화", + "sitemap-topics": "사이트맵에 표시할 토픽 수", + "clear-sitemap-cache": "사이트맵 캐쉬 삭제", + "view-sitemap": "사이트맵" } \ No newline at end of file diff --git a/public/language/ko/category.json b/public/language/ko/category.json index 6e0cb5e177..ad9e576507 100644 --- a/public/language/ko/category.json +++ b/public/language/ko/category.json @@ -10,11 +10,11 @@ "share_this_category": "이 카테고리를 공유", "watch": "관심 주제", "ignore": "관심 해제", - "watching": "Watching", - "ignoring": "Ignoring", + "watching": "관심있음", + "ignoring": "무시하기", "watching.description": "읽지 않은 주제를 표시합니다", "ignoring.description": "읽지 않은 주제를 표시하지 않습니다", - "watch.message": "You are now watching updates from this category and all subcategories", - "ignore.message": "You are now ignoring updates from this category and all subcategories", + "watch.message": "이 게시판 밑 모든 하위게시판의 업데이트에 대해 알림을 받습니다.", + "ignore.message": "이 게시판 밑 모든 하위게시판의 업데이트에 대해 알림을 받지 않습니다.", "watched-categories": "관심 카테고리" } \ No newline at end of file diff --git a/public/language/ko/email.json b/public/language/ko/email.json index 79259b2a40..3a35e5d41b 100644 --- a/public/language/ko/email.json +++ b/public/language/ko/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "이 게시물 알림은 사용자의 구독 설정에 따라 전송되었습니다.", "test.text1": "이 시험용 메일은 NodeBB에 설정된 메일 송신자가 정상적으로 메일을 송신할 수 있는지 시험할 목적으로 발송되었습니다.", "unsub.cta": "설정을 변경하려면 여기를 클릭하세요.", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "귀하는 %1 로 부터 차단되었습니다.", + "banned.text1": "사용자 %1 는 %2 로 부터 차단되었습니다.", + "banned.text2": "차단은 %1 까지 유효합니다.", + "banned.text3": "귀하가 차단된 이유는:", "closing": "감사합니다!" } \ No newline at end of file diff --git a/public/language/ko/error.json b/public/language/ko/error.json index f67dae56bb..40715f2083 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -1,132 +1,134 @@ { "invalid-data": "올바르지 않은 정보입니다.", + "invalid-json": "올바르지 않은 JSON 형식입니다.", "not-logged-in": "로그인하지 않았습니다.", "account-locked": "임시로 잠긴 계정입니다.", "search-requires-login": "검색을 하기 위해서는 계정이 필요합니다. 로그인하거나 가입해 주십시오.", - "invalid-cid": "올바르지 않은 카테고리 ID입니다.", - "invalid-tid": "올바르지 않은 주제 ID입니다.", - "invalid-pid": "올바르지 않은 게시물 ID입니다.", + "invalid-cid": "올바르지 않은 게시판 ID입니다.", + "invalid-tid": "올바르지 않은 게시물 ID입니다.", + "invalid-pid": "올바르지 않은 포스트 ID입니다.", "invalid-uid": "올바르지 않은 사용자 ID입니다.", - "invalid-username": "올바르지 않은 사용자 이름입니다.", + "invalid-username": "올바르지 않은 사용자명 입니다.", "invalid-email": "올바르지 않은 이메일입니다.", "invalid-title": "올바르지 않은 제목입니다.", "invalid-user-data": "올바르지 않은 사용자 정보입니다.", "invalid-password": "올바르지 않은 비밀번호입니다.", - "invalid-username-or-password": "사용자 이름과 패스워드를 모두 설정해주세요.", + "invalid-login-credentials": "올바르지 않은 로그인 정보입니다.", + "invalid-username-or-password": "사용자명과 패스워드를 모두 설정해주세요.", "invalid-search-term": "올바르지 않은 검색어입니다.", - "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", - "invalid-pagination-value": "올바르지 않은 값입니다. 최소 1%에서 최대 2%까지 설정해야 합니다.", - "username-taken": "이미 사용 중인 사용자 이름입니다.", + "csrf-invalid": "세션이 만료되어 로그인에 실패하였습니다. 다시 시도해 주세요.", + "invalid-pagination-value": "올바르지 않은 페이지 값입니다. 최소 1% 에서 최대 2% 사이로 설정해야 합니다.", + "username-taken": "이미 사용 중인 사용자명 입니다.", "email-taken": "이미 사용 중인 이메일입니다.", "email-not-confirmed": "아직 이메일이 인증되지 않았습니다. 여기를 누르면 인증 메일을 발송할 수 있습니다.", "email-not-confirmed-chat": "아직 이메일이 인증되지 않았습니다. 대화기능은 인증 후에 사용이 가능합니다.", - "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email.", + "email-not-confirmed-email-sent": "아직 이메일이 인증되지 않았습니다. 메일함을 확인 해주세요.", "no-email-to-confirm": "이메일 인증이 필요합니다. 이곳을 클릭하여 이메일 입력하세요.", "email-confirm-failed": "이메일 인증이 실패하였습니다. 잠시 후에 다시 시도하세요.", "confirm-email-already-sent": "인증 메일이 이미 발송되었습니다. %1 분 이후에 재 발송이 가능합니다.", - "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", + "sendmail-not-found": "Sendmail 실행파일을 찾을 수 없었습니다. 사용자가 sendmail을 설치했고 실행 가능한지 확인해 주시기 바랍니다.", "username-too-short": "사용자 이름이 너무 짧습니다.", "username-too-long": "사용자 이름이 너무 깁니다.", "password-too-long": "패스워드가 너무 깁니다.", "user-banned": "차단된 사용자입니다.", - "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason": "죄송합니다. 해당 계정은 차단되었습니다. (사유: %1)", + "user-banned-reason-until": "죄송합니다. 해당 계정은 %1 까지 차단되었습니다. (사유: %2)", "user-too-new": "죄송합니다, 첫 번째 게시물은 %1 초 후에 작성할 수 있습니다.", "blacklisted-ip": "죄송하지만, 당신의 IP는 이 커뮤니티로부터 차단되었습니다. 만약 에러라는 생각이 드신다면 관리자에게 연락해주세요.", - "ban-expiry-missing": "Please provide an end date for this ban", - "no-category": "존재하지 않는 카테고리입니다.", - "no-topic": "존재하지 않는 주제입니다.", - "no-post": "존재하지 않는 게시물입니다.", - "no-group": "존재하지 않는 그룹입니다.", - "no-user": "존재하지 않는 사용자입니다.", - "no-teaser": "존재하지 않는 미리보기입니다.", + "ban-expiry-missing": "해당 차단의 만료일을 설정 해주세요.", + "no-category": "존재하지 않는 게시판 입니다.", + "no-topic": "존재하지 않는 게시물 입니다.", + "no-post": "존재하지 않는 포스트 입니다.", + "no-group": "존재하지 않는 그룹 입니다.", + "no-user": "존재하지 않는 사용자 입니다.", + "no-teaser": "존재하지 않는 미리보기 입니다.", "no-privileges": "이 작업을 할 수 있는 권한이 없습니다.", - "category-disabled": "비활성화된 카테고리입니다.", - "topic-locked": "잠긴 주제입니다.", - "post-edit-duration-expired": "게시물의 수정은 작성한 시간으로부터 %1초 후에 가능합니다.", - "post-edit-duration-expired-minutes": "You are only allowed to edit posts for %1 minute(s) after posting", - "post-edit-duration-expired-minutes-seconds": "You are only allowed to edit posts for %1 minute(s) %2 second(s) after posting", - "post-edit-duration-expired-hours": "You are only allowed to edit posts for %1 hour(s) after posting", - "post-edit-duration-expired-hours-minutes": "You are only allowed to edit posts for %1 hour(s) %2 minute(s) after posting", - "post-edit-duration-expired-days": "You are only allowed to edit posts for %1 day(s) after posting", - "post-edit-duration-expired-days-hours": "You are only allowed to edit posts for %1 day(s) %2 hour(s) after posting", - "post-delete-duration-expired": "You are only allowed to delete posts for %1 second(s) after posting", - "post-delete-duration-expired-minutes": "You are only allowed to delete posts for %1 minute(s) after posting", - "post-delete-duration-expired-minutes-seconds": "You are only allowed to delete posts for %1 minute(s) %2 second(s) after posting", - "post-delete-duration-expired-hours": "You are only allowed to delete posts for %1 hour(s) after posting", - "post-delete-duration-expired-hours-minutes": "You are only allowed to delete posts for %1 hour(s) %2 minute(s) after posting", - "post-delete-duration-expired-days": "You are only allowed to delete posts for %1 day(s) after posting", - "post-delete-duration-expired-days-hours": "You are only allowed to delete posts for %1 day(s) %2 hour(s) after posting", - "cant-delete-topic-has-reply": "You can't delete your topic after it has a reply", - "cant-delete-topic-has-replies": "You can't delete your topic after it has %1 replies", - "content-too-short": "게시물의 내용이 너무 짧습니다. 내용은 최소 %1자 이상이어야 합니다.", - "content-too-long": "게시물의 내용이 너무 깁니다. 내용은 최대 %1자 이내로 작성할 수 있습니다.", + "category-disabled": "게시판이 비활성화 되었습니다.", + "topic-locked": "게시물이 잠겼습니다.", + "post-edit-duration-expired": "포스트의 수정은 작성한 시간으로부터 %1 초 후에 가능합니다.", + "post-edit-duration-expired-minutes": "포스트의 수정은 작성한 시간으로부터 %1분 후에 가능합니다.", + "post-edit-duration-expired-minutes-seconds": "포스트의 수정은 작성한 시간으로부터 %1분 %2초 후에 가능합니다.", + "post-edit-duration-expired-hours": "포스트의 수정은 작성한 시간으로부터 %1시간 후에 가능합니다.", + "post-edit-duration-expired-hours-minutes": "포스트의 수정은 작성한 시간으로부터 %1시간 %2분 후에 가능합니다.", + "post-edit-duration-expired-days": "포스트의 수정은 작성한 시간으로부터 %1일 후에 가능합니다.", + "post-edit-duration-expired-days-hours": "포스트의 수정은 작성한 시간으로부터 %1일 %2시간 후에 가능합니다.", + "post-delete-duration-expired": "포스트의 삭제는 작성한 시간으로부터 %1초 후에 가능합니다.", + "post-delete-duration-expired-minutes": "포스트의 삭제는 작성한 시간으로부터 %1분 후에 가능합니다.", + "post-delete-duration-expired-minutes-seconds": "포스트의 삭제는 작성한 시간으로부터 %1분 %2초 후에 가능합니다.", + "post-delete-duration-expired-hours": "포스트의 삭제는 작성한 시간으로부터 %1시간 후에 가능합니다.", + "post-delete-duration-expired-hours-minutes": "포스트의 삭제는 작성한 시간으로부터 %1시간 %2분 후에 가능합니다.", + "post-delete-duration-expired-days": "포스트의 삭제는 작성한 시간으로부터 %1일 후에 가능합니다.", + "post-delete-duration-expired-days-hours": "포스트의 삭제는 작성한 시간으로부터 %1일 %2시간 후에 가능합니다.", + "cant-delete-topic-has-reply": "답글이 달린 게시물은 삭제하실 수 없습니다.", + "cant-delete-topic-has-replies": "답글이 %1개 이상 달린 게시물은 삭제하실 수 없습니다.", + "content-too-short": "포스트의 내용이 너무 짧습니다. 내용은 최소 %1자 이상이어야 합니다.", + "content-too-long": "포스트의 내용이 너무 깁니다. 내용은 최대 %1자 이내로 작성할 수 있습니다.", "title-too-short": "제목이 너무 짧습니다. 제목은 최소 %1자 이상이어야 합니다.", "title-too-long": "제목이 너무 깁니다. 제목은 최대 %1자 이내로 작성할 수 있습니다.", - "category-not-selected": "Category not selected.", + "category-not-selected": "선택된 게시판이 없습니다.", "too-many-posts": "새 게시물 작성은 %1초마다 가능합니다 - 조금 천천히 작성해주세요.", "too-many-posts-newbie": "신규 사용자는 %2 만큼의 인지도를 얻기 전까지 %1초마다 게시물을 작성할 수 있습니다. 조금 천천히 작성해주세요.", - "tag-too-short": "꼬리표가 너무 짧습니다. 꼬리표는 최소 %1자 이상이어야 합니다.", - "tag-too-long": "꼬리표가 너무 깁니다. 꼬리표는 최대 %1자 이내로 사용가능합니다.", - "not-enough-tags": "꼬리표가 없거나 부족합니다. 게시물은 %1개 이상의 꼬리표를 사용해야 합니다.", - "too-many-tags": "꼬리표가 너무 많습니다. 게시물은 %1개 이하의 꼬리표를 사용할 수 있습니다.", + "tag-too-short": "태그가 너무 짧습니다. 태그는 최소 %1자 이상이어야 합니다.", + "tag-too-long": "태그가 너무 깁니다. 태그는 최대 %1자 이내로 사용가능합니다.", + "not-enough-tags": "태그가 없거나 부족합니다. 게시물은 %1개 이상의 태그를 사용해야 합니다.", + "too-many-tags": "태그가 너무 많습니다. 게시물은 %1개 이하의 태그를 사용할 수 있습니다.", "still-uploading": "업로드가 끝날 때까지 기다려주세요.", "file-too-big": "업로드 가능한 파일크기는 최대 %1 KB 입니다 - 파일의 용량을 줄이거나 압축을 활용하세요.", - "guest-upload-disabled": "손님의 파일 업로드는 제한되어 있습니다.", - "already-bookmarked": "You have already bookmarked this post", - "already-unbookmarked": "You have already unbookmarked this post", + "guest-upload-disabled": "미가입 사용자의 파일 업로드는 제한되어 있습니다.", + "already-bookmarked": "이미 즐겨찾기에 추가한 포스트 입니다.", + "already-unbookmarked": "이미 즐겨찾기를 해제한 포스트 입니다.", "cant-ban-other-admins": "다른 관리자를 차단할 수 없습니다.", - "cant-remove-last-admin": "귀하는 유일한 관리자입니다. 관리자를 그만두시기 전에 다른 사용자를 관리자로 선임하세요.", - "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", + "cant-remove-last-admin": "귀하는 유일한 관리자입니다. 관리자를 그만두시기 전에 다른 사용자를 관리자로 임명하세요.", + "cant-delete-admin": "해당 계정을 삭제하기 전에 관리자 권한을 해제 해주십시오.", "invalid-image-type": "올바르지 않은 이미지입니다. 사용가능한 유형: %1", "invalid-image-extension": "올바르지 않은 이미지 확장자입니다.", "invalid-file-type": "올바르지 않은 파일 유형입니다. 사용가능한 유형: %1", "group-name-too-short": "그룹 이름이 너무 짧습니다.", - "group-name-too-long": "Group name too long", + "group-name-too-long": "그룹 이름이 너무 깁니다.", "group-already-exists": "이미 존재하는 그룹입니다.", - "group-name-change-not-allowed": "그룹 이름의 변경은 불가합니다.", + "group-name-change-not-allowed": "그룹 이름의 변경이 불가능 합니다.", "group-already-member": "이미 이 그룹에 속해있습니다.", "group-not-member": "이 그룹의 구성원이 아닙니다.", "group-needs-owner": "이 그룹은 적어도 한 명의 소유자가 필요합니다.", "group-already-invited": "이 사용자는 이미 초대됐습니다.", "group-already-requested": "회원 요청이 이미 제출되었습니다.", - "post-already-deleted": "이미 삭제된 게시물입니다.", - "post-already-restored": "이미 복원된 게시물입니다.", - "topic-already-deleted": "이미 삭제된 주제입니다.", - "topic-already-restored": "이미 복원된 주제입니다.", - "cant-purge-main-post": "주요 게시물은 삭제할 수 없습니다. 대신 주제를 삭제하세요.", - "topic-thumbnails-are-disabled": "주제 섬네일이 이미 해제되었습니다.", + "post-already-deleted": "이미 삭제된 포스트 입니다.", + "post-already-restored": "이미 복원된 포스트 입니다.", + "topic-already-deleted": "이미 삭제된 게시물 입니다.", + "topic-already-restored": "이미 복원된 게시물 입니다.", + "cant-purge-main-post": "메인 포스트는 삭제할 수 없습니다. 대신 게시물을 삭제하세요.", + "topic-thumbnails-are-disabled": "게시물 섬네일이 비활성화 되었습니다.", "invalid-file": "올바르지 않은 파일입니다.", - "uploads-are-disabled": "업로드는 비활성화되어 있습니다.", - "signature-too-long": "서명은 %1자 이내로 작성할 수 있습니다.", - "about-me-too-long": "자기소개는 %1자 이내로 작성할 수 있습니다.", + "uploads-are-disabled": "업로드가 비활성화 되었습니다.", + "signature-too-long": "서명은 %1 자를 넘길 수 없습니다.", + "about-me-too-long": "자기소개는 %1 자를 넘길 수 없습니다.", "cant-chat-with-yourself": "자신과는 대화할 수 없습니다.", - "chat-restricted": "이 사용자는 대화를 제한하고 있습니다. 대화하려면 이 사용자가 귀하를 따라야합니다.", - "chat-disabled": "대화 기능을 사용하지 않습니다.", + "chat-restricted": "이 사용자는 대화를 제한하고 있습니다. 대화하려면 이 사용자가 귀하를 팔로우 해야합니다.", + "chat-disabled": "채팅 시스템이 비활성화 되었습니다.", "too-many-messages": "짧은 시간동안 너무 많은 메시지를 전송하였습니다. 잠시 후에 다시 시도하세요.", "invalid-chat-message": "올바르지 않은 메시지입니다.", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", - "cant-edit-chat-message": "편집 할 수 있는 권한이 없습니다.", + "chat-message-too-long": "채팅 메세지는 최대 %1자로 제한됩니다.", + "cant-edit-chat-message": "이 메세지를 수정 할 권한이 없습니다.", "cant-remove-last-user": "마지막 사용자를 삭제할 수 없습니다.", - "cant-delete-chat-message": "메세지를 지울 권한이 없습니다.", - "already-voting-for-this-post": "이미 이 게시글에 투표하셨습니다.", - "reputation-system-disabled": "인지도 기능이 비활성 상태입니다.", + "cant-delete-chat-message": "이 메세지를 삭제할 권한이 없습니다.", + "already-voting-for-this-post": "이미 이 포스트에 투표하셨습니다.", + "reputation-system-disabled": "인지도 시스템이 비활성 상태입니다.", "downvoting-disabled": "비추천 기능이 비활성 상태입니다.", - "not-enough-reputation-to-downvote": "인지도가 낮아 이 게시물에 반대할 수 없습니다.", - "not-enough-reputation-to-flag": "인지도가 낮아 이 게시물을 신고할 수 없습니다.", + "not-enough-reputation-to-downvote": "인지도가 낮아 이 포스트를 비추천할 수 없습니다.", + "not-enough-reputation-to-flag": "인지도가 낮아 이 포스트를 신고할 수 없습니다.", "already-flagged": "이미 이 게시물을 신고했습니다.", "reload-failed": "NodeBB 서버를 다시 읽어들이는 중 다음과 같은 문제가 발생했으나 사용자측은 지속적으로 자원을 제공받습니다. 오류 문구: \"%1\" 문제를 해결하시려면 다시 읽어들이기 전의 수정사항을 원래대로 되돌려주세요. ", "registration-error": "등록 오류", - "parse-error": "서버 응답을 파싱하는 동안 문제가 발생했습니다.", + "parse-error": "서버로 부터의 응답을 읽는 동안 문제가 발생했습니다.", "wrong-login-type-email": "이메일 주소를 통해 로그인하세요.", "wrong-login-type-username": "사용자명을 통해 로그인하세요.", - "invite-maximum-met": "초대가능한 사용자를 모두 초대했습니다. (%2명 중 %1을 초대)", + "invite-maximum-met": "초대 한도 만큼의 사용자를 초대했습니다. (%2명 중 %1을 초대)", "no-session-found": "로그인 세션을 찾을 수 없습니다.", "not-in-room": "없는 사용자입니다.", "no-users-in-room": "사용자가 없습니다.", "cant-kick-self": "스스로 이 그룹을 탈퇴할 수 없습니다.", - "no-users-selected": "No user(s) selected", - "invalid-home-page-route": "Invalid home page route", - "invalid-session": "Session Mismatch", - "invalid-session-text": "It looks like your login session is no longer active, or no longer matches with the server. Please refresh this page." + "no-users-selected": "선택된 사용자가 없습니다.", + "invalid-home-page-route": "올바르지 않은 홈페이지 경로입니다. ", + "invalid-session": "일치하지 않는 세션입니다.", + "invalid-session-text": "로그인 세션이 비활성화 되었거나 서버와 일치하지 않습니다. 페이지를 새로 고쳐주세요." } \ No newline at end of file diff --git a/public/language/ko/flags.json b/public/language/ko/flags.json index c4bfed6f23..8693130936 100644 --- a/public/language/ko/flags.json +++ b/public/language/ko/flags.json @@ -1,60 +1,60 @@ { - "state": "State", - "reporter": "Reporter", - "reported-at": "Reported At", - "description": "Description", - "no-flags": "Hooray! No flags found.", - "assignee": "Assignee", - "update": "Update", - "updated": "Updated", - "target-purged": "The content this flag referred to has been purged and is no longer available.", + "state": "처리 상태", + "reporter": "신고자", + "reported-at": "신고시간", + "description": "설명", + "no-flags": "들어온 신고가 없습니다.", + "assignee": "담당자", + "update": "업데이트", + "updated": "업데이트 되었습니다.", + "target-purged": "해당 신고된 컨텐츠는 완전 삭제 되었으며, 더이상 존재하지 않습니다.", - "quick-filters": "Quick Filters", - "filter-active": "There are one or more filters active in this list of flags", - "filter-reset": "Remove Filters", - "filters": "Filter Options", - "filter-reporterId": "Reporter UID", - "filter-targetUid": "Flagged UID", - "filter-type": "Flag Type", - "filter-type-all": "All Content", - "filter-type-post": "Post", - "filter-state": "State", - "filter-assignee": "Assignee UID", - "filter-cid": "Category", - "filter-quick-mine": "Assigned to me", - "filter-cid-all": "All categories", - "apply-filters": "Apply Filters", + "quick-filters": "간편 필터", + "filter-active": "적용된 하나 이상의 필터가 있습니다.", + "filter-reset": "필터 제거", + "filters": "필터 옵션", + "filter-reporterId": "신고자 ID", + "filter-targetUid": "신고된 게시물 ID", + "filter-type": "신고 유형", + "filter-type-all": "모든 컨텐츠", + "filter-type-post": "포스트", + "filter-state": "처리 상태", + "filter-assignee": "담당자 ID", + "filter-cid": "게시판", + "filter-quick-mine": "나에게 배정된 신고", + "filter-cid-all": "모든 게시판", + "apply-filters": "필터 적용", - "quick-links": "Quick Links", - "flagged-user": "Flagged User", - "view-profile": "View Profile", - "start-new-chat": "Start New Chat", - "go-to-target": "View Flag Target", + "quick-links": "바로가기", + "flagged-user": "신고된 사용자", + "view-profile": "프로필 보기", + "start-new-chat": "새로운 채팅 시작", + "go-to-target": "신고된 게시물 바로가기", - "user-view": "View Profile", - "user-edit": "Edit Profile", + "user-view": "프로필 보기", + "user-edit": "프로필 수정", - "notes": "Flag Notes", - "add-note": "Add Note", - "no-notes": "No shared notes.", + "notes": "관리자 피드백", + "add-note": "노트 추가", + "no-notes": "공유된 노트가 없습니다.", - "history": "Flag History", - "back": "Back to Flags List", - "no-history": "No flag history.", + "history": "신고 기록", + "back": "신고 목록으로 돌아가기", + "no-history": "신고 기록들이 없습니다.", - "state-all": "All states", - "state-open": "New/Open", - "state-wip": "Work in Progress", - "state-resolved": "Resolved", - "state-rejected": "Rejected", - "no-assignee": "Not Assigned", - "note-added": "Note Added", + "state-all": "모든 상태", + "state-open": "새로 생성된 신고", + "state-wip": "처리중", + "state-resolved": "처리됨", + "state-rejected": "거절된 신고", + "no-assignee": "담당자 미정", + "note-added": "노트가 추가되었습니다.", - "modal-title": "Report Inappropriate Content", - "modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.", - "modal-reason-spam": "Spam", - "modal-reason-offensive": "Offensive", - "modal-reason-custom": "Reason for reporting this content...", - "modal-submit": "Submit Report", - "modal-submit-success": "Content has been flagged for moderation." + "modal-title": "부적절한 컨텐츠 신고", + "modal-body": "%1 %2 에 대한 신고 사유를 적어주시거나, 빠른 신고 버튼 중 하나를 사용해 주세요.", + "modal-reason-spam": "스팸", + "modal-reason-offensive": "부적절한 글", + "modal-reason-custom": "신고 사유", + "modal-submit": "리포트 제출", + "modal-submit-success": "이 컨텐츠는 신고되었습니다." } \ No newline at end of file diff --git a/public/language/ko/global.json b/public/language/ko/global.json index 10be4e5a94..283bed5f68 100644 --- a/public/language/ko/global.json +++ b/public/language/ko/global.json @@ -3,33 +3,33 @@ "search": "검색", "buttons.close": "닫기", "403.title": "접근이 거부되었습니다.", - "403.message": "접속할 수 없는 페이지에 접근하였습니다.", + "403.message": "권한이 없는 페이지에 접속을 시도하였습니다.", "403.login": "로그인되어 있는지 확인해 주세요.", "404.title": "페이지를 찾을 수 없습니다.", "404.message": "존재하지 않는 페이지에 접근하였습니다. 홈 페이지로 이동합니다.", - "500.title": "Internal Error.", + "500.title": "내부 오류", "500.message": "알 수 없는 오류가 발생했습니다.", - "400.title": "Bad Request.", - "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.", + "400.title": "잘못된 요청", + "400.message": "해당 링크는 잘못된 형식입니다. 다시 한번 확인 후 시도해 주십시오. 아니면 홈 페이지로 이동합니다. ", "register": "회원가입", "login": "로그인", "please_log_in": "로그인해 주세요.", "logout": "로그아웃", - "posting_restriction_info": "게시물 작성은 현재 회원에게만 제한되고 있습니다. 여기를 누르면 로그인 페이지로 이동합니다.", + "posting_restriction_info": "현재 회원들만 게시물을 작성 할 수 있습니다.여기를 누르면 로그인 페이지로 이동합니다.", "welcome_back": "환영합니다.", "you_have_successfully_logged_in": "성공적으로 로그인했습니다.", "save_changes": "저장", - "save": "Save", + "save": "저장", "close": "닫기", "pagination": "페이지", - "pagination.out_of": "%2개 중 %1개", + "pagination.out_of": "%2 개 중 %1 개", "pagination.enter_index": "이동할 게시물 번호를 입력하세요.", "header.admin": "관리자", - "header.categories": "카테고리", - "header.recent": "최근 주제", - "header.unread": "읽지 않은 주제", - "header.tags": "꼬리표", - "header.popular": "인기 주제", + "header.categories": "게시판", + "header.recent": "최근 게시물", + "header.unread": "읽지 않은 게시물", + "header.tags": "태그", + "header.popular": "인기 게시물", "header.users": "사용자", "header.groups": "그룹", "header.chats": "채팅", @@ -46,36 +46,36 @@ "alert.error": "오류", "alert.banned": "차단됨", "alert.banned.message": "이 계정은 차단되었습니다. 지금 로그아웃됩니다.", - "alert.unfollow": "더 이상 %1님을 팔로우하지 않습니다.", - "alert.follow": "%1님을 팔로우합니다.", + "alert.unfollow": "더 이상 %1 님을 팔로우 하지않습니다.", + "alert.follow": "%1 님을 팔로우 합니다.", "online": "온라인", "users": "사용자", - "topics": "주제", - "posts": "게시물", + "topics": "게시물", + "posts": "포스트", "best": "베스트", - "upvoters": "Upvoters", - "upvoted": "Upvoted", - "downvoters": "Downvoters", - "downvoted": "비추됨", + "upvoters": "추천한 사용자", + "upvoted": "추천된 게시물", + "downvoters": "비추천한 사용자", + "downvoted": "비추천된 게시물", "views": "조회 수", - "reputation": "인기도", - "read_more": "전체 보기", + "reputation": "등급", + "read_more": "더 보기", "more": "더 보기", - "posted_ago_by_guest": "익명 사용자가 %1에 작성했습니다.", - "posted_ago_by": "%2님이 %1에 작성했습니다.", - "posted_ago": "%1에 작성되었습니다.", - "posted_in": "%1에 작성되었습니다.", - "posted_in_by": "%2님이 %1에 작성했습니다.", - "posted_in_ago": "%2 %1에 작성되었습니다. ", - "posted_in_ago_by": "%3님이 %2 %1에 작성했습니다.", - "user_posted_ago": "%1님이 %2에 작성했습니다.", - "guest_posted_ago": "익명 사용자가 %1에 작성했습니다.", - "last_edited_by": "%1님이 마지막으로 수정했습니다.", - "norecentposts": "최근 작성된 게시물이 없습니다.", - "norecenttopics": "최근 생성된 생성된 주제가 없습니다.", - "recentposts": "최근 게시물", - "recentips": "최근 로그인 IP", - "moderator_tools": "Moderator Tools", + "posted_ago_by_guest": "미가입 사용자가 %1 에 작성했습니다.", + "posted_ago_by": "%2 님이 %1 에 작성했습니다.", + "posted_ago": "%1 에 작성되었습니다.", + "posted_in": "%1 에 작성되었습니다.", + "posted_in_by": "%2 님이 %1 에 작성했습니다.", + "posted_in_ago": "%2 %1 에 작성되었습니다. ", + "posted_in_ago_by": "%3 님이 %2 %1 에 작성했습니다.", + "user_posted_ago": "%1 님이 %2 에 작성했습니다.", + "guest_posted_ago": "미가입 사용자가 %1 에 작성했습니다.", + "last_edited_by": "%1 님이 마지막으로 수정했습니다.", + "norecentposts": "최근 작성된 포스트가 없습니다.", + "norecenttopics": "최근 작성된 게시물이 없습니다.", + "recentposts": "최근 포스트", + "recentips": "최근에 로그인한 IP", + "moderator_tools": "(준)관리자 도구모음", "away": "자리 비움", "dnd": "방해 금지", "invisible": "오프라인으로 표시", @@ -85,7 +85,7 @@ "guest": "익명 사용자", "guests": "익명 사용자", "updated.title": "포럼이 업데이트 되었습니다.", - "updated.message": "이 포럼은 지금 최신 버전으로 업데이트 되었습니다. 여기를 누르면 페이지를 새로고침합니다.", + "updated.message": "이 포럼은 지금 최신 버전으로 업데이트 되었습니다. 페이지를 새로고침 하시려면 여기를 클릭해주세요.", "privacy": "개인정보", "follow": "팔로우", "unfollow": "언팔로우", @@ -97,11 +97,13 @@ "upload_file": "파일 업로드", "upload": "업로드", "allowed-file-types": "사용가능한 파일 유형: %1", - "unsaved-changes": "You have unsaved changes. Are you sure you wish to navigate away?", - "reconnecting-message": "Looks like your connection to %1 was lost, please wait while we try to reconnect.", - "play": "Play", - "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", - "cookies.accept": "Got it!", - "cookies.learn_more": "Learn More", - "edited": "Edited" + "unsaved-changes": "저장되지 않은 변경사항이 있습니다. 저장하지 않고 페이지를 떠나시겠습니까?", + "reconnecting-message": "%1 사이트로의 연결이 끊어졌습니다. 다시 연결을 시도하는동안 잠시만 기다려 주십시오.", + "play": "재생", + "cookies.message": "이 웹사이트는 최적의 사용환경을 위해 쿠키를 활용합니다.", + "cookies.accept": "알겠습니다!", + "cookies.learn_more": "더 보기", + "edited": "수정 되었습니다.", + "disabled": "비활성화", + "select": "선택" } \ No newline at end of file diff --git a/public/language/ko/groups.json b/public/language/ko/groups.json index b4f7ccd636..6b7c393ee2 100644 --- a/public/language/ko/groups.json +++ b/public/language/ko/groups.json @@ -1,33 +1,33 @@ { "groups": "그룹", "view_group": "그룹 보기", - "owner": "그룹관리자", - "new_group": "그룹 생성", + "owner": "그룹 관리자", + "new_group": "새로운 그룹 만들기", "no_groups_found": "그룹이 없습니다.", "pending.accept": "수락", "pending.reject": "거절", "pending.accept_all": "전체 수락", "pending.reject_all": "전체 거절", - "pending.none": "지금은 승인대기 회원이 없습니다.", + "pending.none": "지금은 승인 대기중인 회원이 없습니다.", "invited.none": "지금은 초대된 회원이 없습니다.", - "invited.uninvite": "초대를 철회", - "invited.search": "그룹에 초대할 사용자를 검색하세요.", + "invited.uninvite": "초대 취소", + "invited.search": "그룹에 초대할 사용자 검색", "invited.notification_title": "%1 그룹에 초대되었습니다.", - "request.notification_title": "%1 님으로부터 그룹 구성원 요청이 들어왔습니다.", - "request.notification_text": "%1 님이 %2 의 멤버 가입을 신청했습니다.", + "request.notification_title": "%1 님으로부터 그룹 가입 요청이 들어왔습니다.", + "request.notification_text": "%1 님이 %2 에 가입을 신청했습니다.", "cover-save": "저장", "cover-saving": "저장 중", "details.title": "그룹 상세정보", "details.members": "구성원 목록", - "details.pending": "대기 구성원", + "details.pending": "승인 대기중인 구성원", "details.invited": "초대된 구성원", "details.has_no_posts": "이 그룹의 구성원이 작성한 게시물이 없습니다.", - "details.latest_posts": "최근 게시물", + "details.latest_posts": "최근 포스트", "details.private": "비공개", "details.disableJoinRequests": "가입 신청 비활성화하기", "details.grant": "소유권 이전/포기하기", "details.kick": "내보내기", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "이 사용자를 해당 그룹에서 삭제 하시겠습니까?", "details.owner_options": "그룹 관리", "details.group_name": "그룹명", "details.member_count": "구성원 수", @@ -38,7 +38,7 @@ "details.change_colour": "컬러 변경", "details.badge_text": "배지 문구", "details.userTitleEnabled": "배지 보이기", - "details.private_help": "활성 후 구성원 가입시 그룹 관리자의 승인이 필요합니다.", + "details.private_help": "활성 시, 구성원 가입시 그룹 관리자의 승인이 필요합니다.", "details.hidden": "숨김", "details.hidden_help": "활성 시 그룹 목록에 노출되지 않습니다. 또한 구성원은 초대를 통해서만 가입이 가능합니다.", "details.delete_group": "그룹 삭제", @@ -51,8 +51,8 @@ "membership.leave-group": "그룹 나가기", "membership.reject": "거절", "new-group.group_name": "그룹명:", - "upload-group-cover": "그룹 커버 업로드", - "bulk-invite-instructions": "Enter a list of comma separated usernames to invite to this group", - "bulk-invite": "Bulk Invite", - "remove_group_cover_confirm": "Are you sure you want to remove the cover picture?" + "upload-group-cover": "그룹 커버 사진 업로드", + "bulk-invite-instructions": "초대하고자 하는 사용자 목록을 콤마(,) 로 구분하여 기입해 주십시오.\n예: bravominski, stjohndlee, yoojkim", + "bulk-invite": "다수의 사용자 초대", + "remove_group_cover_confirm": "해당 커버 사진을 제거 하시겠습니까?" } \ No newline at end of file diff --git a/public/language/ko/login.json b/public/language/ko/login.json index 9245c84185..a1691ee955 100644 --- a/public/language/ko/login.json +++ b/public/language/ko/login.json @@ -1,12 +1,12 @@ { - "username-email": "사용자 이름 / 이메일", + "username-email": "사용자명 / 이메일", "username": "사용자명", "email": "이메일", "remember_me": "로그인 유지", - "forgot_password": "비밀번호 초기화", + "forgot_password": "비밀번호 재설정", "alternative_logins": "다른 방법으로 로그인", - "failed_login_attempt": "Login Unsuccessful", + "failed_login_attempt": "로그인 실패", "login_successful": "성공적으로 로그인했습니다.", "dont_have_account": "계정이 없으신가요?", - "logged-out-due-to-inactivity": "You have been logged out of the Admin Control Panel due to inactivity" + "logged-out-due-to-inactivity": "일정시간 활동하지 않아 관리자 제어판에서 로그아웃 되었습니다." } \ No newline at end of file diff --git a/public/language/ko/modules.json b/public/language/ko/modules.json index e85fbc9763..521e73bed6 100644 --- a/public/language/ko/modules.json +++ b/public/language/ko/modules.json @@ -1,45 +1,45 @@ { - "chat.chatting_with": "님과의 채팅", + "chat.chatting_with": " 님과의 채팅", "chat.placeholder": "메시지를 여기에 입력한 후 엔터를 눌러 전송하세요.", "chat.send": "전송", "chat.no_active": "활성화된 채팅이 없습니다.", - "chat.user_typing": "%1님이 입력 중입니다.", - "chat.user_has_messaged_you": "%1님이 메시지를 보냈습니다.", - "chat.see_all": "모든 대화 보기", - "chat.mark_all_read": "읽은 채팅 읽음으로 표시", - "chat.no-messages": "대화 기록을 보려면 대화 상대를 선택하세요.", + "chat.user_typing": "%1 님이 입력 중입니다.", + "chat.user_has_messaged_you": "%1 님이 메시지를 보냈습니다.", + "chat.see_all": "모든 채팅 보기", + "chat.mark_all_read": "모든 채팅 읽음으로 표시", + "chat.no-messages": "채팅 기록을 보려면 채팅 상대를 선택하세요.", "chat.no-users-in-room": "사용자가 없습니다.", - "chat.recent-chats": "최근 대화 내용", + "chat.recent-chats": "최근 채팅", "chat.contacts": "연락처", "chat.message-history": "대화 기록", - "chat.pop-out": "분리된 창에서 대화", - "chat.minimize": "Minimize", + "chat.pop-out": "분리된 창에서 채팅", + "chat.minimize": "최소화", "chat.maximize": "최대화", "chat.seven_days": "7일", "chat.thirty_days": "30일", "chat.three_months": "3개월", - "chat.delete_message_confirm": "이 대화를 삭제하시겠습니까?", - "chat.add-users-to-room": "유저 추가하기", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.delete_message_confirm": "이 메세지를 삭제 하시겠습니까?", + "chat.add-users-to-room": "사용자 추가하기", + "chat.confirm-chat-with-dnd-user": "이 사용자의 상태는 \"방해금지\" 입니다. 그래도 대화를 요청 하시겠습니까?", "composer.compose": "작성", "composer.show_preview": "미리보기", "composer.hide_preview": "미리보기 숨김", - "composer.user_said_in": "%1님이 %2에서 한 말:", - "composer.user_said": "%1님의 말:", - "composer.discard": "이 게시물을 지우겠습니까?", - "composer.submit_and_lock": "잠금 상태로 작성 완료", + "composer.user_said_in": "%1 님이 %2 에서 보낸 메세지:", + "composer.user_said": "%1 님의 메세지:", + "composer.discard": "이 포스트를 삭제 하시겠습니까?", + "composer.submit_and_lock": "글 작성 후 잠금", "composer.toggle_dropdown": "내려서 확인하기", "composer.uploading": "%1 업로드 중", - "composer.formatting.bold": "Bold", - "composer.formatting.italic": "Italic", - "composer.formatting.list": "List", - "composer.formatting.strikethrough": "Strikethrough", - "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", - "composer.upload-picture": "Upload Image", - "composer.upload-file": "Upload File", - "composer.zen_mode": "Zen Mode", - "composer.select_category": "Select a category", + "composer.formatting.bold": "굵은글씨", + "composer.formatting.italic": "이탤릭체", + "composer.formatting.list": "목록", + "composer.formatting.strikethrough": "취소선", + "composer.formatting.link": "링크", + "composer.formatting.picture": "사진", + "composer.upload-picture": "이미지 업로드", + "composer.upload-file": "파일 업로드", + "composer.zen_mode": "전체화면", + "composer.select_category": "카테고리 선택", "bootbox.ok": "확인", "bootbox.cancel": "취소", "bootbox.confirm": "확인", diff --git a/public/language/ko/notifications.json b/public/language/ko/notifications.json index 68830845d1..cb3b29e88d 100644 --- a/public/language/ko/notifications.json +++ b/public/language/ko/notifications.json @@ -3,46 +3,46 @@ "no_notifs": "새 알림이 없습니다.", "see_all": "모든 알림 보기", "mark_all_read": "모든 알림을 읽음 상태로 변경", - "back_to_home": "%1로 돌아가기", + "back_to_home": "%1 로 돌아가기", "outgoing_link": "외부 링크", - "outgoing_link_message": "%1 에서 나오셨습니다.", + "outgoing_link_message": "%1 를 떠납니다.", "continue_to": "%1 사이트로 이동", "return_to": "%1 사이트로 돌아가기", "new_notification": "새 알림", "you_have_unread_notifications": "읽지 않은 알림이 있습니다.", - "all": "All", - "topics": "Topics", - "replies": "Replies", - "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "all": "모든 알림", + "topics": "게시물", + "replies": "답글", + "chat": "채팅", + "follows": "팔로우", + "upvote": "추천", + "new-flags": "새로 들어온 신고", + "my-flags": "내게 배정된 신고", + "bans": "차단", "new_message_from": "%1님이 메시지를 보냈습니다.", - "upvoted_your_post_in": "%1님이 %2의 내 게시물을 추천했습니다.", - "upvoted_your_post_in_dual": "%1님과 %2님이 %3의 내 게시물을 추천했습니다.", - "upvoted_your_post_in_multiple": "%1 님과 다른 %2 명이 %3의 내 게시물을 추천했습니다.", - "moved_your_post": "%1님이 귀하의 게시물을 %2로 옮겼습니다.", - "moved_your_topic": "%1%2 로 옮겨졌습니다.", - "user_flagged_post_in": "%1님이 %2의 게시물을 신고했습니다.", - "user_flagged_post_in_dual": "%1 님과 %2 님이 %3 안의 게시물에 플래그를 세웠습니다.", - "user_flagged_post_in_multiple": "%1 님과 %2 명의 다른 유저들이 %3 안의 게시물에 플래그를 세웠습니다.", - "user_flagged_user": "%1 flagged a user profile (%2)", - "user_flagged_user_dual": "%1 and %2 flagged a user profile (%3)", - "user_flagged_user_multiple": "%1 and %2 others flagged a user profile (%3)", - "user_posted_to": "%1님이 %2에 답글을 작성했습니다.", + "upvoted_your_post_in": "%1님이 %2의 내 포스트를 추천했습니다.", + "upvoted_your_post_in_dual": "%1님과 %2님이 %3의 내 포스트를 추천했습니다.", + "upvoted_your_post_in_multiple": "%1 님과 다른 %2 명이 %3의 내 포스트를 추천했습니다.", + "moved_your_post": "%1님이 귀하의 포스트를 %2로 옮겼습니다.", + "moved_your_topic": "%1%2 를 옮겼습니다.", + "user_flagged_post_in": "%1님이 %2에 속한 포스트를 신고했습니다.", + "user_flagged_post_in_dual": "%1 님과 %2 님이 %3 에 속한 포스트를 신고했습니다.", + "user_flagged_post_in_multiple": "%1 님과 %2 명의 다른 유저들이 %3 에 속한 포스트를 신고했습니다.", + "user_flagged_user": "%1님이 %2의 프로필을 신고했습니다.", + "user_flagged_user_dual": "%1님과 %2님이 %3의 프로필을 신고했습니다.", + "user_flagged_user_multiple": "%1 님과 다른 %2 명이 %3의 프로필을 신고했습니다.", + "user_posted_to": "%1님이 %2 에 답글을 달았습니다.", "user_posted_to_dual": "%1 님과 %2 님이 %3 에 답글을 달았습니다.", "user_posted_to_multiple": "%1 님과 %2 명의 다른 유저들이 %3 에 답글을 달았습니다.", - "user_posted_topic": "%1님이 새 주제를 작성했습니다: %2", - "user_started_following_you": "%1님이 나를 팔로우합니다.", - "user_started_following_you_dual": "%1님과 %2님이 당신을 팔로우 시작했습니다.", - "user_started_following_you_multiple": "%1님외 %2명이 당신을 팔로우 시작했습니다.", + "user_posted_topic": "%1님이 새 게시물을 작성했습니다: %2", + "user_started_following_you": "%1님이 나를 팔로우 합니다.", + "user_started_following_you_dual": "%1님과 %2님이 나를 팔로우 합니다.", + "user_started_following_you_multiple": "%1님외 %2명이 나를 팔로우 합니다.", "new_register": "%1님이 가입요청을 했습니다.", - "new_register_multiple": "%1 개의 회원가입 요청이 승인 대기중입니다.", - "flag_assigned_to_you": "Flag %1 has been assigned to you", - "email-confirmed": "확인된 이메일", - "email-confirmed-message": "이메일을 확인해주셔서 감사합니다. 계정이 완전히 활성화되었습니다.", - "email-confirm-error-message": "이메일 주소를 검증하지 못했습니다. 코드가 올바르지 않거나 만료되었을 수 있습니다.", + "new_register_multiple": "%1 개의 회원가입 요청이 승인 대기중입니다.", + "flag_assigned_to_you": "신고 ID %1 이 나에게 배정되었습니다.", + "email-confirmed": "이메일 인증 되었습니다", + "email-confirmed-message": "이메일을 인증해주셔서 감사합니다. 계정이 완전히 활성화되었습니다.", + "email-confirm-error-message": "이메일 주소를 인증하지 못했습니다. 코드가 올바르지 않거나 만료 되었을 수 있습니다.", "email-confirm-sent": "확인 이메일이 발송되었습니다." } \ No newline at end of file diff --git a/public/language/ko/pages.json b/public/language/ko/pages.json index 2d258932ac..5bcd9ba573 100644 --- a/public/language/ko/pages.json +++ b/public/language/ko/pages.json @@ -1,51 +1,51 @@ { "home": "홈", - "unread": "읽지 않은 주제", - "popular-day": "인기있는 주제 (오늘)", - "popular-week": "인기있는 주제 (주간)", - "popular-month": "인기있는 주제 (월간)", - "popular-alltime": "인기있는 주제", - "recent": "최근 주제", - "flagged-content": "Flagged Content", - "ip-blacklist": "IP Blacklist", - "users/online": "온라인 사용자", + "unread": "읽지 않은 게시물", + "popular-day": "인기있는 게시물 (오늘)", + "popular-week": "인기있는 게시물 (주간)", + "popular-month": "인기있는 게시물 (월간)", + "popular-alltime": "인기있는 게시물", + "recent": "최근 게시물", + "flagged-content": "신고된 컨텐츠", + "ip-blacklist": "IP 블랙리스트", + "users/online": "접속중인 사용자", "users/latest": "최근 사용자", "users/sort-posts": "가장 많은 게시물을 작성한 사용자", - "users/sort-reputation": "가장 높은 인지도를 가진 사용자", - "users/banned": "차단된 유저", - "users/most-flags": "Most flagged users", + "users/sort-reputation": "가장 높은 등급을 가진 사용자", + "users/banned": "차단된 사용자", + "users/most-flags": "가장 많이 신고된 사용자", "users/search": "사용자 검색", "notifications": "알림", "tags": "태그", - "tag": "\"%1\" 꼬리표가 달린 주제", + "tag": "\"%1\" 태그가 달린 게시물", "register": "회원가입", - "registration-complete": "Registration complete", + "registration-complete": "회원가입 완료", "login": "로그인", - "reset": "패스워드 초기화", - "categories": "카테고리", + "reset": "패스워드 재설정", + "categories": "게시판", "groups": "그룹", "group": "%1 그룹", - "chats": "대화", - "chat": "%1 님과 대화", - "flags": "Flags", - "flag-details": "Flag %1 Details", - "account/edit": "\"%1\" 편집", - "account/edit/password": "\"%1\" 의 패스워드 변경", - "account/edit/username": "\"%1\" 의 사용자명 변경", - "account/edit/email": "\"%1\" 의 이메일 변경", - "account/info": "Account Info", - "account/following": "%1 명이 팔로우", - "account/followers": "%1 명을 팔로우", - "account/posts": "%1 님이 작성한 게시물", - "account/topics": "%1 님이 생성한 주제", - "account/groups": "%1님의 그룹", - "account/bookmarks": "%1's Bookmarked Posts", + "chats": "채팅", + "chat": "%1 님과 채팅", + "flags": "신고 목록", + "flag-details": "신고 ID %1 의 세부내용", + "account/edit": "\"%1\" 수정", + "account/edit/password": "\"%1\" 의 패스워드 수정", + "account/edit/username": "\"%1\" 의 사용자명 수정", + "account/edit/email": "\"%1\" 의 이메일 수정", + "account/info": "계정 정보", + "account/following": "%1 님이 팔로우 하는 사용자", + "account/followers": "%1 님을 팔로우 하는 사용자", + "account/posts": "%1 님이 작성한 포스트", + "account/topics": "%1 님이 생성한 게시물", + "account/groups": "%1 님의 그룹", + "account/bookmarks": "%1 님이 즐겨찾기 한 포스트", "account/settings": "사용자 설정", - "account/watched": "%1님이 지켜보는 주제", - "account/upvoted": "%1 님이 upvote한 게시물", - "account/downvoted": "%1 님에 의해 Downvote된 게시물", - "account/best": "%1 님 최고의 게시물", - "confirm": "확인된 이메일", + "account/watched": "%1 님이 관심 가진 게시물", + "account/upvoted": "%1 님이 추천한 포스트", + "account/downvoted": "%1 님에 비추천한 포스트", + "account/best": "%1 님 최고의 포스트", + "confirm": "이메일 인증 되었습니다", "maintenance.text": "%1 사이트는 현재 점검 중입니다. 나중에 다시 방문해주세요.", "maintenance.messageIntro": "다음은 관리자가 전하는 메시지입니다.", "throttled.text": "과도한 부하로 %1 를 로드할 수 없습니다. 잠시후에 다시 시도해주세요." diff --git a/public/language/ko/recent.json b/public/language/ko/recent.json index 2c795f1970..5773b12a48 100644 --- a/public/language/ko/recent.json +++ b/public/language/ko/recent.json @@ -1,19 +1,19 @@ { - "title": "최근 주제", + "title": "최근 게시글", "day": "일간", "week": "주간", "month": "월간", "year": "연간", "alltime": "전체", - "no_recent_topics": "최근 생성된 주제가 없습니다.", - "no_popular_topics": "인기 주제가 없습니다.", - "there-is-a-new-topic": "새로운 주제가 있습니다.", - "there-is-a-new-topic-and-a-new-post": "새로운 주제와 게시물이 있습니다.", - "there-is-a-new-topic-and-new-posts": "새로운 주제와 %1 개의 게시물이 있습니다.", - "there-are-new-topics": "새로운 %1 개의 주제가 있습니다.", - "there-are-new-topics-and-a-new-post": "새로운 %1 개의 주제와 게시물이 있습니다.", - "there-are-new-topics-and-new-posts": "새로운 %1 개의 주제와 %2 개의 게시물이 있습니다.", - "there-is-a-new-post": "새로운 게시물이 있습니다.", - "there-are-new-posts": "새로운 %1 개의 게시물이 있습니다.", + "no_recent_topics": "최근 생성된 게시물이 없습니다.", + "no_popular_topics": "인기 게시물이 없습니다.", + "there-is-a-new-topic": "새로운 게시물이 있습니다.", + "there-is-a-new-topic-and-a-new-post": "새로운 게시물과 포스트가 있습니다.", + "there-is-a-new-topic-and-new-posts": "새로운 게시물과 %1 개의 포스트가 있습니다.", + "there-are-new-topics": "새로운 %1 개의 게시물이 있습니다.", + "there-are-new-topics-and-a-new-post": "새로운 %1 개의 게시물과 포스트가 있습니다.", + "there-are-new-topics-and-new-posts": "새로운 %1 개의 게시물과 %2 개의 포스트가 있습니다.", + "there-is-a-new-post": "새로운 포스트가 있습니다.", + "there-are-new-posts": "새로운 %1 개의 포스트가 있습니다.", "click-here-to-reload": "이 곳을 클릭하여 새로고침 하세요." } \ No newline at end of file diff --git a/public/language/ko/register.json b/public/language/ko/register.json index a2be020b58..6f2d19f5b2 100644 --- a/public/language/ko/register.json +++ b/public/language/ko/register.json @@ -1,23 +1,23 @@ { "register": "회원가입", - "cancel_registration": "Cancel Registration", + "cancel_registration": "회원가입 취소", "help.email": "입력하신 이메일 주소는 공개되지 않으며, 설정을 통해 공개하실 수 있습니다.", - "help.username_restrictions": "%1자 이상 %2자 이하의 고유한 이름을 입력하세요. @username 같은 방식으로 다른 사람들을 언급할 수 있습니다.", + "help.username_restrictions": "%1자 이상 %2자 이하의 고유한 사용자명을 입력하세요. @username 같은 방식으로 다른 사람들을 언급할 수 있습니다.", "help.minimum_password_length": "비밀번호는 최소 %1자로 제한됩니다.", "email_address": "이메일", "email_address_placeholder": "여기에 이메일 주소를 입력하세요.", - "username": "사용자 이름", - "username_placeholder": "여기에 사용자 이름을 입력하세요.", + "username": "사용자명", + "username_placeholder": "여기에 사용자명을 입력하세요.", "password": "비밀번호", "password_placeholder": "여기에 비밀번호를 입력하세요.", - "confirm_password": "비밀번호 재입력", - "confirm_password_placeholder": "여기에 비밀번호를 재입력하세요.", - "register_now_button": "회원가입", + "confirm_password": "비밀번호 확인", + "confirm_password_placeholder": "여기에 비밀번호 확인을 입력하세요.", + "register_now_button": "가입하기", "alternative_registration": "다른 방법으로 회원가입", "terms_of_use": "이용약관", "agree_to_terms_of_use": "이용약관에 동의합니다.", - "terms_of_use_error": "You must agree to the Terms of Use", + "terms_of_use_error": "이용약관에 동의하셔야 합니다.", "registration-added-to-queue": "회원가입이 요청되었습니다. 관리자의 승인 후 메일이 발송됩니다.", - "interstitial.intro": "We require some additional information before we can create your account.", - "interstitial.errors-found": "We could not complete your registration:" + "interstitial.intro": "계정을 생성하기 전 추가 정보가 더 필요합니다.", + "interstitial.errors-found": "회원가입을 완료하지 못했습니다." } \ No newline at end of file diff --git a/public/language/ko/reset_password.json b/public/language/ko/reset_password.json index 6701e2fa7d..98e774c668 100644 --- a/public/language/ko/reset_password.json +++ b/public/language/ko/reset_password.json @@ -1,17 +1,17 @@ { - "reset_password": "비밀번호 초기화", - "update_password": "비밀번호 변경", - "password_changed.title": "비밀번호가 변경되었습니다.", - "password_changed.message": "

성공적으로 비밀번호가 초기화되었습니다. 다시 로그인해 주세요.", + "reset_password": "패스워드 재설정", + "update_password": "패스워드 변경", + "password_changed.title": "패스워드가 변경되었습니다.", + "password_changed.message": "

성공적으로 패스워드가 재설정 되었습니다. 다시 로그인해 주세요.", "wrong_reset_code.title": "올바르지 않은 초기화 코드입니다.", - "wrong_reset_code.message": "올바르지 않은 초기화 코드입니다. 다시 시도하거나 새로운 초기화 코드를 요청하세요.", - "new_password": "새 비밀번호", - "repeat_password": "비밀번호 재입력", - "enter_email": "이메일 주소를 입력하면 비밀번호를 초기화하는 방법을 메일로 알려드립니다.", - "enter_email_address": "여기에 이메일 주소를 입력하세요.", - "password_reset_sent": "이메일이 발송되었습니다.", + "wrong_reset_code.message": "올바르지 않은 재설정 코드입니다. 다시 시도하거나 새로운 재설정 코드를 요청하세요.", + "new_password": "새 패스워드", + "repeat_password": "패스워드 확인", + "enter_email": "이메일 주소를 입력하면 패스워드를 재설정하는 방법을 메일로 알려드립니다.", + "enter_email_address": "이메일 주소를 입력하세요.", + "password_reset_sent": "패스워드 재설정 이메일이 발송되었습니다.", "invalid_email": "올바르지 않거나 가입되지 않은 이메일입니다.", - "password_too_short": "입력한 비밀번호가 너무 짧습니다, 다시 입력하세요.", - "passwords_do_not_match": "비밀번호와 재입력한 비밀번호가 일치하지 않습니다.", - "password_expired": "비밀번호가 만료되었습니다, 새로운 비밀번호를 입력하세요." + "password_too_short": "입력한 패스워드가 너무 짧습니다, 다시 입력하세요.", + "passwords_do_not_match": "패스워드와 패스워드 확인이 일치하지 않습니다.", + "password_expired": "패스워드가 만료되었습니다, 새로운 패스워드를 입력하세요." } \ No newline at end of file diff --git a/public/language/ko/search.json b/public/language/ko/search.json index 0dde0601e2..41d207e08c 100644 --- a/public/language/ko/search.json +++ b/public/language/ko/search.json @@ -1,18 +1,18 @@ { - "results_matching": "\"%2\"와 일치하는 %1개의 결과를 찾았습니다 (검색시간: %3초)", + "results_matching": "\"%2\"와 일치하는 %1 개의 결과를 찾았습니다 (검색시간: %3초)", "no-matches": "일치하는 결과가 없습니다.", "advanced-search": "고급 검색", - "in": "안에서 검색", + "in": "검색 기준", "titles": "제목", - "titles-posts": "제목과 게시물", - "posted-by": "다음 사용자에 의해 작성됨", - "in-categories": "카테고리 내", - "search-child-categories": "하위 카테고리를 검색", - "has-tags": "Has tags", + "titles-posts": "제목과 내용", + "posted-by": "작성자", + "in-categories": "게시판 지정", + "search-child-categories": "하위 게시판도 검색", + "has-tags": "태그로 검색", "reply-count": "답변 수", "at-least": "적어도", "at-most": "최대", - "relevance": "Relevance", + "relevance": "관련도", "post-time": "작성시간", "newer-than": "이전", "older-than": "이후", @@ -24,19 +24,19 @@ "three-months": "세 달", "six-months": "여섯 달", "one-year": "일 년", - "sort-by": "정렬", + "sort-by": "정렬 기준", "last-reply-time": "마지막으로 답글이 달린 시간", - "topic-title": "주제 제목", + "topic-title": "게시물 제목", "number-of-replies": "답글 수", "number-of-views": "조회 수", - "topic-start-date": "주제가 게시된 날짜", + "topic-start-date": "게시물이 작성된 날짜", "username": "사용자명", - "category": "카테고리", + "category": "게시판", "descending": "내림차순", "ascending": "오름차순", "save-preferences": "설정 저장", "clear-preferences": "설정 초기화", "search-preferences-saved": "검색 설정이 저장되었습니다.", "search-preferences-cleared": "검색 설정이 초기화되었습니다.", - "show-results-as": "검색결과 정렬 - " + "show-results-as": "검색결과 표시방법" } \ No newline at end of file diff --git a/public/language/ko/success.json b/public/language/ko/success.json index e7b911bb2d..051a6c2299 100644 --- a/public/language/ko/success.json +++ b/public/language/ko/success.json @@ -2,5 +2,5 @@ "success": "성공", "topic-post": "성공적으로 게시물을 작성했습니다.", "authentication-successful": "인증에 성공했습니다.", - "settings-saved": "성공적으로 설정을 저장했습니다." + "settings-saved": "설정이 저장되었습니다!" } \ No newline at end of file diff --git a/public/language/ko/tags.json b/public/language/ko/tags.json index d088ef7041..aa0f44e62c 100644 --- a/public/language/ko/tags.json +++ b/public/language/ko/tags.json @@ -1,7 +1,7 @@ { - "no_tag_topics": "이 태그에 해당하는 주제가 없습니다.", - "tags": "태그", - "enter_tags_here": "%1 와 %2 글자 안에서 태그를 입력하세요.", + "no_tag_topics": "이 태그가 달린 게시물이 없습니다.", + "tags": "태그 목록", + "enter_tags_here": "%1 에서 %2 자 안에서 태그를 입력하세요.", "enter_tags_here_short": "태그 입력...", - "no_tags": "아직 아무런 태그도 없습니다." + "no_tags": "아직 태그가 달리지 않았습니다." } \ No newline at end of file diff --git a/public/language/ko/topic.json b/public/language/ko/topic.json index 4b6775f329..11d0d8fc7e 100644 --- a/public/language/ko/topic.json +++ b/public/language/ko/topic.json @@ -1,119 +1,119 @@ { - "topic": "주제", - "topic_id": "주제 ID", - "topic_id_placeholder": "여기에 주제 ID를 입력하세요.", - "no_topics_found": "주제를 찾을 수 없습니다.", - "no_posts_found": "게시물을 찾을 수 없습니다.", - "post_is_deleted": "이 게시물은 삭제되었습니다.", - "topic_is_deleted": "이 주제는 삭제되었습니다.", + "topic": "게시물", + "topic_id": "게시물 ID", + "topic_id_placeholder": "여기에 게시물 ID를 입력하세요.", + "no_topics_found": "게시물을 찾을 수 없습니다!", + "no_posts_found": "포스트를 찾을 수 없습니다!", + "post_is_deleted": "이 포스트는 삭제됐습니다!", + "topic_is_deleted": "이 포스트는 삭제됐습니다!", "profile": "프로필", - "posted_by": "%1님이 작성했습니다.", - "posted_by_guest": "익명 사용자가 작성했습니다.", + "posted_by": "%1 님에 의해 작성됨", + "posted_by_guest": "미가입 사용자에 의해 작성됨", "chat": "채팅", - "notify_me": "이 주제의 새 답글 알리기", + "notify_me": "이 게시물의 새 답글에 대한 알림 받기", "quote": "인용", "reply": "답글", - "replies_to_this_post": "%1 Replies", - "last_reply_time": "Last reply", - "reply-as-topic": "주제로 답글", - "guest-login-reply": "답변을 위해 로그인하기", + "replies_to_this_post": "%1 개의 답글", + "one_reply_to_this_post": "1 개의 답글", + "last_reply_time": "마지막 답글", + "reply-as-topic": "게시물로 답글하기", + "guest-login-reply": "답글을 작성하기 위해 로그인", "edit": "수정", "delete": "삭제", "purge": "폐기", "restore": "복원", "move": "이동", "fork": "분리", - "link": "링크", + "link": "바로가기", "share": "공유", "tools": "도구", - "locked": "잠김", - "pinned": "Pinned", - "moved": "Moved", - "bookmark_instructions": "이 스레드에서 읽은 마지막 게시글로 이동하시려면 여기를 클릭하세요.", - "flag_title": "이 게시물을 신고", - "deleted_message": "이 주제는 삭제되었습니다. 주제 관리 권한이 있는 사용자만 볼 수 있습니다.", - "following_topic.message": "이제 이 주제에 새 답글이 달리면 알림을 받습니다.", - "not_following_topic.message": "You will see this topic in the unread topics list, but you will not receive notifications when somebody posts to this topic.", - "ignoring_topic.message": "You will no longer see this topic in the unread topics list. You will be notified when you are mentioned or your post is up voted.", - "login_to_subscribe": "이 주제의 알림을 받기 위해서는 로그인해야 합니다.", + "locked": "잠긴 게시물", + "pinned": "고정된 게시물", + "moved": "이동된 게시물", + "bookmark_instructions": "이 스레드에서 읽은 마지막 포스트로 이동하시려면 여기를 클릭 하세요.", + "flag_title": "이 포스트를 신고", + "deleted_message": "이 게시물은 삭제됐습니다. 게시물 관리 권한이 있는 사용자만 볼 수 있습니다.", + "following_topic.message": "이제 이 게시물에 새 답글이 달리면 알림을 받습니다.", + "not_following_topic.message": "이 게시물을 아직 읽지 않은 게시물 목록에서 볼 수 있지만, 이 주제에 달린 포스트에 대해서는 알림을 받지 않습니다.", + "ignoring_topic.message": "더 이상 이 게시물은 읽지 않은 게시물 목록에서 보이지 않습니다. 누군가 나를 언급하거나 내 포스트가 추천 받으면 알림을 받습니다.", + "login_to_subscribe": "이 게시물을 관심 목록에 추가하기 위해서는 로그인해야 합니다.", "markAsUnreadForAll.success": "모든 사용자에 대해 읽지 않음으로 표시했습니다.", "mark_unread": "읽지 않음으로 표시", - "mark_unread.success": "성공적으로 읽지 않음으로 표시했습니다.", - "watch": "관심 주제", - "unwatch": "관심 주제 해제", - "watch.title": "이 주제의 새 답글 알리기", - "unwatch.title": "이 주제에 대한 관심을 해제합니다.", - "share_this_post": "이 게시물 공유", - "watching": "Watching", - "not-watching": "Not Watching", - "ignoring": "Ignoring", - "watching.description": "Notify me of new replies.
Show topic in unread.", - "not-watching.description": "Do not notify me of new replies.
Show topic in unread if category is not ignored.", - "ignoring.description": "Do not notify me of new replies.
Do not show topic in unread.", - "thread_tools.title": "주제 관리", - "thread_tools.markAsUnreadForAll": "Mark unread for all", + "mark_unread.success": "게시물을 읽지 않음으로 표시했습니다", + "watch": "관심 목록에 추가", + "unwatch": "관심 목록에서 제거", + "watch.title": "이 게시물의 새 답글에 대해 알림 받기", + "unwatch.title": "이 게시물을 관심 목록에서 제거합니다.", + "share_this_post": "이 포스트를 공유", + "watching": "관심 있음", + "not-watching": "관심 없음", + "ignoring": "무시하는 중", + "watching.description": "새로운 답글에 대한 알림 받기.
읽지 않은 게시물에 보여주기.", + "not-watching.description": "새로운 답글에 대해 알림 받지 않기. 해당 게시판을 팔로우 중이라면 \"읽지않은 게시물\" 에서 보여주기.", + "ignoring.description": "새로운 답글에 대한 알림 받지 않기. \"읽지않은 게시물\"에서 보여주지 않기.", + "thread_tools.title": "게시물 관리", + "thread_tools.markAsUnreadForAll": "모든 사용자에게 읽지 않음으로 표시", "thread_tools.pin": "상단 고정", "thread_tools.unpin": "상단 고정 해제", "thread_tools.lock": "잠금", "thread_tools.unlock": "잠금 해제", "thread_tools.move": "이동", "thread_tools.move_all": "모두 이동", + "thread_tools.select_category": "게시판 선택", "thread_tools.fork": "주제 분리", "thread_tools.delete": "삭제", - "thread_tools.delete-posts": "게시물 삭제", - "thread_tools.delete_confirm": "이 주제를 삭제하시겠습니까?", - "thread_tools.restore": "복원", - "thread_tools.restore_confirm": "이 주제를 복원하시겠습니까?", - "thread_tools.purge": "주제 폐기", - "thread_tools.purge_confirm": "이 주제를 폐기하시겠습니까?", - "topic_move_success": "성공적으로 이 주제를 %1로 이동했습니다.", - "post_delete_confirm": "이 게시물을 삭제하시겠습니까?", - "post_restore_confirm": "이 게시물을 복원하시겠습니까?", - "post_purge_confirm": "이 게시물을 폐기하시겠습니까?", - "load_categories": "카테고리 로딩 중", - "disabled_categories_note": "비활성화된 카테고리는 회색으로 표시됩니다.", + "thread_tools.delete-posts": "포스트 삭제", + "thread_tools.delete_confirm": "이 게시물을 삭제 하시겠습니까?", + "thread_tools.restore": "게시물 복원", + "thread_tools.restore_confirm": "이 게시물을 복원 하시겠습니까?", + "thread_tools.purge": "게시물 폐기", + "thread_tools.purge_confirm": "이 게시물을 폐기 하시겠습니까?", + "topic_move_success": "성공적으로 이 게시물을 %1로 이동했습니다.", + "post_delete_confirm": "이 포스트를 삭제 하시겠습니까?", + "post_restore_confirm": "이 포스트를 복원 하시겠습니까?", + "post_purge_confirm": "이 포스트를 폐기 하시겠습니까?", + "load_categories": "게시판들을 읽어오는 중입니다.", "confirm_move": "이동", "confirm_fork": "분리", - "bookmark": "Bookmark", - "bookmarks": "Bookmarks", - "bookmarks.has_no_bookmarks": "You haven't bookmarked any posts yet.", - "loading_more_posts": "게시물을 로딩 중", - "move_topic": "주제 이동", - "move_topics": "주제 이동", - "move_post": "게시물 이동", - "post_moved": "게시물이 이동되었습니다.", - "fork_topic": "주제 분리", - "topic_will_be_moved_to": "이 주제를 지정한 카테고리로 이동합니다.", - "fork_topic_instruction": "분리할 게시물을 선택하세요.", - "fork_no_pids": "게시물이 선택되지 않았습니다.", - "fork_pid_count": "%1 post(s) selected", - "fork_success": "주제가 분리되었습니다! 분리된 주제를 보려면 여기를 클릭하세요.", - "delete_posts_instruction": "삭제할 게시물을 선택하세요.", - "composer.title_placeholder": "여기에 제목을 입력하세요.", + "bookmark": "즐겨찾기", + "bookmarks": "즐겨찾기 목록", + "bookmarks.has_no_bookmarks": "즐겨찾기에 추가한 포스트가 없습니다.", + "loading_more_posts": "포스트를 읽어오는 중입니다.", + "move_topic": "게시물 이동", + "move_topics": "게시물 이동", + "move_post": "포스트 이동", + "post_moved": "포스트가 이동되었습니다.", + "fork_topic": "게시물 분리", + "fork_topic_instruction": "분리할 포스트를 선택하세요.", + "fork_no_pids": "선택된 포스트가 없습니다.", + "fork_pid_count": "%1 개의 포스트(들)이 선택되었습니다", + "fork_success": "게시물이 분리되었습니다! 분리된 게시물을 보려면 여기를 클릭 하세요.", + "delete_posts_instruction": "삭제할 포스트를 선택하세요.", + "composer.title_placeholder": "게시물 제목을 입력하세요.", "composer.handle_placeholder": "이름", "composer.discard": "취소", "composer.submit": "등록", "composer.replying_to": "'%1'에 대한 답글", "composer.new_topic": "새 주제 생성", "composer.uploading": "업로드 중", - "composer.thumb_url_label": "섬네일 URL", - "composer.thumb_title": "이 주제에 섬네일 추가", + "composer.thumb_url_label": "섬네일 URL을 붙여넣으세요", + "composer.thumb_title": "이 게시물에 섬네일 추가", "composer.thumb_url_placeholder": "http://example.com/thumb.png", - "composer.thumb_file_label": "혹은 파일 업로드", + "composer.thumb_file_label": "혹은 파일을 업로드 해주세요", "composer.thumb_remove": "섬네일 삭제", "composer.drag_and_drop_images": "이미지를 여기에 드래그&드롭하세요.", "more_users_and_guests": "%1명 이상의 회원과 %2명의 익명 사용자", "more_users": "%1명 이상의 회원", "more_guests": "%1명 이상의 익명 사용자", "users_and_others": "%1님 외 %2명", - "sort_by": "정렬", + "sort_by": "정렬 기준", "oldest_to_newest": "오래된 순으로 정렬", "newest_to_oldest": "최신 순으로 정렬", "most_votes": "추천수 순으로 정렬", "most_posts": "포스트 많은 순으로 정렬", - "stale.title": "새로 주제를 생성하시겠습니까?", - "stale.warning": "현재 답글을 작성중인 주제가 꽤 오래되었습니다. 새로 주제를 생성하시고 이글을 인용하시겠습니까?", - "stale.create": "새로운 주제를 작성", - "stale.reply_anyway": "아무튼 이 주제에 답변해주세요.", + "stale.title": "새로운 게시물을 생성 하시겠습니까?", + "stale.warning": "현재 답글을 작성중인 게시물은 오래전에 작성 되었습니다. 새로 게시물을 생성하시고 이 게시물을 인용 하시겠습니까?", + "stale.create": "새로운 게시물 작성", + "stale.reply_anyway": "이 게시물에 답글 작성", "link_back": "답글: [%1](%2)" } \ No newline at end of file diff --git a/public/language/ko/unread.json b/public/language/ko/unread.json index 527a990c08..362d75cbe9 100644 --- a/public/language/ko/unread.json +++ b/public/language/ko/unread.json @@ -1,13 +1,13 @@ { - "title": "읽지 않은 주제", - "no_unread_topics": "읽지 않은 주제가 없습니다.", + "title": "읽지 않음", + "no_unread_topics": "읽지 않은 게시물이 없습니다.", "load_more": "더 보기", "mark_as_read": "읽음으로 표시", - "selected": "선택된 주제", + "selected": "선택된 게시물", "all": "전체", - "all_categories": "모든 카테고리", - "topics_marked_as_read.success": "성공적으로 읽음으로 표시했습니다.", - "all-topics": "모든 주제", - "new-topics": "새 주제", - "watched-topics": "읽은 주제" + "all_categories": "모든 게시판", + "topics_marked_as_read.success": "게시물들을 읽음으로 표시했습니다.", + "all-topics": "모든 게시물", + "new-topics": "새 게시물", + "watched-topics": "관심있는 게시물" } \ No newline at end of file diff --git a/public/language/ko/user.json b/public/language/ko/user.json index f54cadc53e..7ae2e74f74 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -1,136 +1,136 @@ { - "banned": "차단됨", + "banned": "차단된 계정", "offline": "오프라인", "username": "사용자 이름", "joindate": "가입일", - "postcount": "게시물 작성 수", + "postcount": "포스트 수", "email": "이메일", "confirm_email": "이메일 확인", - "account_info": "Account Info", - "ban_account": "차단된 계정", + "account_info": "계정 정보", + "ban_account": "계정 차단", "ban_account_confirm": "이 사용자를 차단하시겠습니까?", "unban_account": "차단 해제", "delete_account": "계정 삭제", - "delete_account_confirm": "정말 계정을 삭제하시겠습니까?
이 동작은 되돌릴 수 없으며 삭제된 데이터도 복구할 수 없습니다.

계정 삭제를 원하면 사용자 이름을 입력하세요.", - "delete_this_account_confirm": "정말 계정을 삭제하시겠습니까?
이 행동은 되돌릴 수 없으며 삭제된 사용자 정보도 복구할 수 없습니다.

", - "account-deleted": "삭제된 계정", + "delete_account_confirm": "정말 계정을 삭제 하시겠습니까?
이 동작은 되돌릴 수 없으며 삭제된 데이터도 복구할 수 없습니다.

계정 삭제를 원하면 사용자 이름을 입력하세요.", + "delete_this_account_confirm": "정말 계정을 삭제 하시겠습니까?
이 행동은 되돌릴 수 없으며 삭제된 사용자 정보도 복구할 수 없습니다.

", + "account-deleted": "계정이 삭제 되었습니다", "fullname": "이름", "website": "웹 사이트", - "location": "거주지", + "location": "위치", "age": "나이", - "joined": "가입일", - "lastonline": "최근 로그인", + "joined": "가입됨", + "lastonline": "최근 온라인 시점", "profile": "프로필", "profile_views": "프로필 조회 수", - "reputation": "인기도", - "bookmarks": "Bookmarks", - "watched": "읽음", - "followers": "이 사용자를 팔로우", - "following": "이 사용자가 팔로우", - "aboutme": "나를 소개", + "reputation": "등급", + "bookmarks": "즐겨찾기", + "watched": "관심있는 게시물", + "followers": "팔로워", + "following": "팔로잉", + "aboutme": "자기소개", "signature": "서명", "birthday": "생일", "chat": "채팅", - "chat_with": "Continue chat with %1", - "new_chat_with": "Start new chat with %1", - "flag-profile": "Flag Profile", + "chat_with": "%1과/와 채팅 계속", + "new_chat_with": "%1과/와 새로운 채팅", + "flag-profile": "프로필 신고", "follow": "팔로우", "unfollow": "팔로우 취소", "more": "더 보기", - "profile_update_success": "성공적으로 프로필을 저장했습니다.", + "profile_update_success": "프로필을 성공적으로 저장했습니다.", "change_picture": "사진 변경", "change_username": "사용자명 변경", "change_email": "이메일 변경", - "edit": "프로필 수정", - "edit-profile": "프로필 수정하기", + "edit": "수정", + "edit-profile": "프로필 수정", "default_picture": "기본 아이콘", - "uploaded_picture": "사진 업로드", + "uploaded_picture": "업로드된 사진", "upload_new_picture": "새 사진 업로드", "upload_new_picture_from_url": "URL을 통해 새 사진 업로드", "current_password": "현재 비밀번호", "change_password": "비밀번호 변경", - "change_password_error": "올바르지 않은 비밀번호", - "change_password_error_wrong_current": "현재 비밀번호가 올바르지 않습니다.", - "change_password_error_length": "비밀번호가 너무 짧습니다.", + "change_password_error": "올바르지 않은 비밀번호입니다!", + "change_password_error_wrong_current": "현재 비밀번호가 일치하지 않습니다!", + "change_password_error_length": "비밀번호가 너무 짧습니다!", "change_password_error_match": "재입력한 비밀번호가 새 비밀번호와 일치하지 않습니다!", "change_password_error_privileges": "비밀번호를 바꿀 권한이 없습니다.", "change_password_success": "비밀번호를 변경했습니다.", - "confirm_password": "비밀번호 재입력", + "confirm_password": "비밀번호 확인", "password": "비밀번호", "username_taken_workaround": "새 사용자 이름이 이미 존재하여 %1로 저장되었습니다.", "password_same_as_username": "비밀번호가 사용자명과 동일합니다. 다른 비밀번호를 입력하세요.", "password_same_as_email": "비밀번호가 이메일 주소와 동일합니다. 다른 비밀번호를 입력하세요.", - "weak_password": "Weak password.", + "weak_password": "보안이 취약한 비밀번호입니다.", "upload_picture": "사진 업로드", "upload_a_picture": "사진 업로드", - "remove_uploaded_picture": "등록된 사진을 삭제", + "remove_uploaded_picture": "업로드한 사진을 삭제", "upload_cover_picture": "커버 사진 업로드", - "remove_cover_picture_confirm": "Are you sure you want to remove the cover picture?", - "crop_picture": "Crop picture", - "upload_cropped_picture": "Crop and upload", + "remove_cover_picture_confirm": "커버 사진을 제거하시겠습니까?", + "crop_picture": "사진 잘라내기", + "upload_cropped_picture": "잘라내고 업로드", "settings": "설정", - "show_email": "이메일 공개", - "show_fullname": "실명 공개", - "restrict_chats": "팔로우 된 사용자만 대화를 허용합니다.", - "digest_label": "포럼 이메일 수신", - "digest_description": "지정한 주기로 이 포럼의 새 알림과 주제를 이메일로 수신합니다.", + "show_email": "내 이메일 보기", + "show_fullname": "내 이름 보기", + "restrict_chats": "내가 팔로우하는 이용자들로부터만 대화를 허용", + "digest_label": "포럼 이메일 구독", + "digest_description": "주기적으로 포럼 이메일(새 알림과 게시물)을 구독", "digest_off": "해제", "digest_daily": "매일", "digest_weekly": "매주", "digest_monthly": "매월", "send_chat_notifications": "오프라인일 때 채팅 메시지가 도착하면 알림 메일을 보냅니다.", - "send_post_notifications": "내가 구독한 주제에 답글이 달리면 메일 보내기.", - "settings-require-reload": "일부 설정 변경은 리로딩이 필요합니다. 여기를 눌러서 페이지를 리로딩 해주세요.", - "has_no_follower": "아무도 이 사용자를 팔로우하지 않습니다.", - "follows_no_one": "이 사용자는 아무도 팔로우하지 않습니다.", - "has_no_posts": "이 사용자가 생성한 게시물이 없습니다.", - "has_no_topics": "이 사용자가 생성한 주제가 없습니다.", - "has_no_watched_topics": "이 유저가 관심 목록에 추가한 주제가 없습니다.", - "has_no_upvoted_posts": "이 유저가 upvote한 게시물이 없습니다.", - "has_no_downvoted_posts": "이 유저가 downvote한 게시물이 없습니다.", - "has_no_voted_posts": "이 유저가 vote한 게시물이 없습니다.", + "send_post_notifications": "내가 관심있는 게시물에 답글이 달리면 메일을 보냅니다.", + "settings-require-reload": "일부 설정 변경은 새로고침이 필요합니다. 여기를 눌러서 페이지를 새로고침 해주세요.", + "has_no_follower": "이 사용자는 팔로워가 없습니다 :(", + "follows_no_one": "이 사용자는 아무도 팔로우하고 있지 않습니다 :(", + "has_no_posts": "이 사용자가 작성한 포스트가 없습니다.", + "has_no_topics": "이 사용자가 작성한 게시물이 없습니다.", + "has_no_watched_topics": "이 사용자가 관심 목록에 추가한 게시물이 없습니다.", + "has_no_upvoted_posts": "이 사용자가 추천한 포스트가 없습니다.", + "has_no_downvoted_posts": "이 사용자가 비추천한 포스트가 없습니다.", + "has_no_voted_posts": "평가받은 게시물이 없습니다.", "email_hidden": "이메일 비공개", "hidden": "비공개", - "paginate_description": "주제와 게시물을 무한 스크롤 대신 페이지뷰로 보기", - "topics_per_page": "페이지 당 주제 수", - "posts_per_page": "페이지 당 게시물 수", + "paginate_description": "주제와 게시물을 페이지로 정리", + "topics_per_page": "페이지 당 게시물 수", + "posts_per_page": "페이지 당 포스트 수", "notification_sounds": "알림 수신시 소리로 알려주기", - "notifications_and_sounds": "Notifications & Sounds", - "incoming-message-sound": "Incoming message sound", - "outgoing-message-sound": "Outgoing message sound", - "notification-sound": "Notification sound", - "no-sound": "No sound", - "browsing": "페이지 열기", - "open_links_in_new_tab": "외부 링크를 새로운 탭을 사용하여 열람", - "enable_topic_searching": "주제 내 검색 허용", - "topic_search_help": "활성화 된후 브라우저의 기본 페이지 검색 기능을 연관 주제 검색 기능으로 대신하고 화면에 보여지는 것 뿐만 아니라 주제와 연관된 모든것을 검색합니다.", + "notifications_and_sounds": "알림 / 알림음 설정", + "incoming-message-sound": "수신 메시지 알림음", + "outgoing-message-sound": "발신 메시지 알림음", + "notification-sound": "알림음", + "no-sound": "음소거", + "browsing": "브라우징 설정", + "open_links_in_new_tab": "외부 링크를 새로운 탭에서 열람", + "enable_topic_searching": "게시물 내 검색 허용", + "topic_search_help": "만약 활성화된다면, 브라우저의 기본 검색 기능은 무효화되고 게시물 내 검색을 통해 화면에 보여지는 것 뿐만 아니라 게시물 전체의 내용을 검색할 수 있습니다.", "delay_image_loading": "이미지 로딩 지연", - "image_load_delay_help": "활성화 되면 이미지 위치로 스크롤을 움직이기 전까지 이미지가 로드 되지 않습니다.", + "image_load_delay_help": "만약 활성화된다면, 이미지 위치로 스크롤을 움직이기 전까지 이미지가 로드 되지 않습니다.", "scroll_to_my_post": "답글 게시 후 새 포스트 보여주기", - "follow_topics_you_reply_to": "Watch topics that you reply to", - "follow_topics_you_create": "Watch topics you create", - "grouptitle": "그룹 주제", + "follow_topics_you_reply_to": "내가 답글을 단 게시물을 관심 목록에 추가", + "follow_topics_you_create": "내가 작성한 게시물을 관심 목록에 추가", + "grouptitle": "그룹 이름", "no-group-title": "그룹 이름이 없습니다.", "select-skin": "스킨 선택", "select-homepage": "홈페이지 선택", "homepage": "홈페이지", "homepage_description": "포럼 홈페이지로 사용할 페이지를 선택하거나 'None'으로 설정하여 기본 홈페이지를 사용합니다.", - "custom_route": "홈페이지 수동 경로", - "custom_route_help": "라우팅 이름을 앞쪽 '/' 없이 입력해주세요 (예: \"최근 목록\", \"인기 게시물\")", + "custom_route": "사용자 지정 홈페이지 라우트", + "custom_route_help": "라우트 이름을 앞쪽 '/' 없이 입력해주세요 (예: \"/recent\" => \"recent\", \"/popular\" => \"popular\")", "sso.title": "통합 인증 서비스", - "sso.associated": "연관짓기 - ", - "sso.not-associated": "이 곳을 클리하여 연관지으세요.", - "info.latest-flags": "Latest Flags", - "info.no-flags": "No Flagged Posts Found", - "info.ban-history": "Recent Ban History", - "info.no-ban-history": "This user has never been banned", - "info.banned-until": "Banned until %1", - "info.banned-permanently": "Banned permanently", - "info.banned-reason-label": "Reason", - "info.banned-no-reason": "No reason given.", - "info.username-history": "Username History", - "info.email-history": "Email History", - "info.moderation-note": "Moderation Note", - "info.moderation-note.success": "Moderation note saved", - "info.moderation-note.add": "Add note" + "sso.associated": "와/과 연동된", + "sso.not-associated": "이 곳을 클릭하여 연동시키세요.", + "info.latest-flags": "최근에 들어온 신고", + "info.no-flags": "신고된 포스트가 없습니다.", + "info.ban-history": "최근 차단 히스토리", + "info.no-ban-history": "이 사용자는 차단된 적이 없습니다.", + "info.banned-until": "%1까지 차단됨", + "info.banned-permanently": "영구 차단", + "info.banned-reason-label": "사유", + "info.banned-no-reason": "제공된 사유가 없습니다.", + "info.username-history": "사용자명 변경 히스토리", + "info.email-history": "이메일 히스토리", + "info.moderation-note": "관리자 노트", + "info.moderation-note.success": "관리자 노트 저장완료", + "info.moderation-note.add": "노트 추가" } \ No newline at end of file diff --git a/public/language/ko/users.json b/public/language/ko/users.json index 738cbe8f05..317fd7e208 100644 --- a/public/language/ko/users.json +++ b/public/language/ko/users.json @@ -1,21 +1,21 @@ { - "latest_users": "가입일", - "top_posters": "게시물 수", - "most_reputation": "인기도", - "most_flags": "Most Flags", + "latest_users": "최근 가입 순", + "top_posters": "작성 게시물 순", + "most_reputation": "사용자 등급 순", + "most_flags": "신고 순", "search": "검색", - "enter_username": "검색할 사용자 이름을 입력하세요.", + "enter_username": "검색할 사용자명을 입력하세요.", "load_more": "더 보기", - "users-found-search-took": "%1명의 사용자를 찾았습니다. 검색 소요 시간 %2 초", - "filter-by": "에 의해 분류", - "online-only": "온라인 전용", + "users-found-search-took": "%1명의 사용자를 찾았습니다. 검색 소요 시간 %2초", + "filter-by": "필터 기준", + "online-only": "접속한 사용자", "invite": "초대", - "invitation-email-sent": "%1님에게 초대 메일을 송신하였습니다.", + "invitation-email-sent": "%1님에게 초대 메일을 보냈습니다.", "user_list": "사용자 목록", - "recent_topics": "최근 주제", - "popular_topics": "인기 주제", - "unread_topics": "읽지 않은 주제", - "categories": "카테고리", + "recent_topics": "최근 게시물", + "popular_topics": "인기 게시물", + "unread_topics": "읽지 않은 게시물", + "categories": "게시판 목록", "tags": "태그", - "no-users-found": "유저를 찾을 수 없습니다!" + "no-users-found": "사용자를 찾을 수 없습니다!" } \ No newline at end of file diff --git a/public/language/lt/admin/appearance/customise.json b/public/language/lt/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/lt/admin/appearance/customise.json +++ b/public/language/lt/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/lt/admin/development/info.json b/public/language/lt/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/lt/admin/development/info.json +++ b/public/language/lt/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/lt/admin/general/languages.json b/public/language/lt/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/lt/admin/general/languages.json +++ b/public/language/lt/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/lt/admin/manage/categories.json b/public/language/lt/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/lt/admin/manage/categories.json +++ b/public/language/lt/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/lt/admin/settings/user.json b/public/language/lt/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/lt/admin/settings/user.json +++ b/public/language/lt/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/lt/error.json b/public/language/lt/error.json index aa16eedd88..18d6f41d29 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Klaidingi duomenys", + "invalid-json": "Invalid JSON", "not-logged-in": "Atrodo, kad jūs neesate prisijungęs.", "account-locked": "Jūsų paskyra buvo laikinai užrakinta", "search-requires-login": "Paieška reikalauja vartotojo - prašome prisijungti arba užsiregistruoti", @@ -12,6 +13,7 @@ "invalid-title": "Neteisingas pavadinimas!", "invalid-user-data": "Klaidingi vartotojo duomenys", "invalid-password": "Klaidingas slaptažodis", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Prašome nurodyti tiek vartotojo vardą, tiek ir slaptažodį", "invalid-search-term": "Neteisingas paieškos terminas", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/lt/global.json b/public/language/lt/global.json index a884569fb3..08bdcf1e87 100644 --- a/public/language/lt/global.json +++ b/public/language/lt/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json index c2e8c8bd56..406d1062b9 100644 --- a/public/language/lt/topic.json +++ b/public/language/lt/topic.json @@ -14,6 +14,7 @@ "quote": "Cituoti", "reply": "Atsakyti", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Norėdami atsakyti, prisijunkite", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Atrakinti temą", "thread_tools.move": "Perkelti temą", "thread_tools.move_all": "Perkelti visus", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Išskaidyti temą", "thread_tools.delete": "Ištrinti temą", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Ar jūs tikrai norite atkurti šį įrašą?", "post_purge_confirm": "Ar tikrai norite išvalyti šį pranešimą?", "load_categories": "Įkeliamos kategorijos", - "disabled_categories_note": "Neaktyvios kategorijos pažymėtos pilkai", "confirm_move": "Perkelti", "confirm_fork": "Išskaidyti", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Perkelti įrašą", "post_moved": "Pranešimas perkeltas!", "fork_topic": "Išskaidyti temą", - "topic_will_be_moved_to": "Ši tema bus perkelta į kategoriją", "fork_topic_instruction": "Pažymėkite ant įrašų, kuriuos norite perkelti į naują temą", "fork_no_pids": "Nepasirinktas joks įrašas!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/ms/admin/appearance/customise.json b/public/language/ms/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/ms/admin/appearance/customise.json +++ b/public/language/ms/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/ms/admin/development/info.json b/public/language/ms/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/ms/admin/development/info.json +++ b/public/language/ms/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/ms/admin/general/languages.json b/public/language/ms/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/ms/admin/general/languages.json +++ b/public/language/ms/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/ms/admin/manage/categories.json b/public/language/ms/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/ms/admin/manage/categories.json +++ b/public/language/ms/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/ms/admin/settings/user.json b/public/language/ms/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/ms/admin/settings/user.json +++ b/public/language/ms/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/ms/error.json b/public/language/ms/error.json index 34f838b4b4..643531b8f0 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Data Tak Sah", + "invalid-json": "Invalid JSON", "not-logged-in": "Anda tidak log masuk.", "account-locked": "Akaun anda telah dikunci untuk seketika", "search-requires-login": "Fungsi Carian perlukan akaun - sila log masuk atau daftar.", @@ -12,6 +13,7 @@ "invalid-title": "Tajuk Tak Sah!", "invalid-user-data": "Data Pengguna Tak Sah", "invalid-password": "Kata laluan salah!", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Sila tentukan kedua-dua nama pengguna dan kata laluan", "invalid-search-term": "Terma pencarian tak sah", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/ms/global.json b/public/language/ms/global.json index e00d937eaa..ce75b91718 100644 --- a/public/language/ms/global.json +++ b/public/language/ms/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json index c1aa71fee9..3f8737d458 100644 --- a/public/language/ms/topic.json +++ b/public/language/ms/topic.json @@ -14,6 +14,7 @@ "quote": "Petikan", "reply": "Balas", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log masuk untuk balas", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Buka kekunci topik", "thread_tools.move": "Pindahkan topik", "thread_tools.move_all": "Pindahkan Semua", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Fork topik", "thread_tools.delete": "Padamkan topik", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Adakah anda pasti untuk memulihkan kiriman ini?", "post_purge_confirm": "Adakah anda pasti untuk singkirkan kiriman ini?", "load_categories": "Memuatkan kategori", - "disabled_categories_note": "Kategori yang disekat diwarnakan kelabu", "confirm_move": "Pindahkan", "confirm_fork": "Salin", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Pindahkan kiriman", "post_moved": "Kiriman dipindahkan", "fork_topic": "Salin topik", - "topic_will_be_moved_to": "Topik ini akan dipindahkan kepada kategori", "fork_topic_instruction": "Klik kiriman yang anda hendak salin", "fork_no_pids": "Tiada kiriman yang dipilih", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/nb/admin/appearance/customise.json b/public/language/nb/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/nb/admin/appearance/customise.json +++ b/public/language/nb/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/nb/admin/development/info.json b/public/language/nb/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/nb/admin/development/info.json +++ b/public/language/nb/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/nb/admin/general/languages.json b/public/language/nb/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/nb/admin/general/languages.json +++ b/public/language/nb/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/nb/admin/manage/categories.json b/public/language/nb/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/nb/admin/manage/categories.json +++ b/public/language/nb/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/nb/admin/settings/user.json b/public/language/nb/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/nb/admin/settings/user.json +++ b/public/language/nb/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index 8c63511a83..a88fdd69c7 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Ugyldige data", + "invalid-json": "Invalid JSON", "not-logged-in": "Du ser ikke ut til å være logget inn.", "account-locked": "Kontoen din har blitt midlertidig låst", "search-requires-login": "Søking krever en konto - vennligst logg inn eller registrer deg.", @@ -12,6 +13,7 @@ "invalid-title": "Ugyldig tittel!", "invalid-user-data": "Ugyldig brukerdata", "invalid-password": "Ugyldig passord", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Vennligst spesifiser både et brukernavn og passord", "invalid-search-term": "Ugyldig søkeord", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/nb/global.json b/public/language/nb/global.json index 5c57f2f251..86fb8d7bfe 100644 --- a/public/language/nb/global.json +++ b/public/language/nb/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/nb/topic.json b/public/language/nb/topic.json index 3c6153e359..031f366c51 100644 --- a/public/language/nb/topic.json +++ b/public/language/nb/topic.json @@ -14,6 +14,7 @@ "quote": "Siter", "reply": "Svar", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Logg inn for å besvare", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Lås opp tråd", "thread_tools.move": "Flytt tråd", "thread_tools.move_all": "Flytt alle", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Forgren tråd", "thread_tools.delete": "Slett tråd", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Er du sikker på at du vil gjenopprette dette innlegget?", "post_purge_confirm": "Er du sikker på at du vil renske dette innlegget?", "load_categories": "Laster kategorier", - "disabled_categories_note": "Deaktiverte kategorier er grået ut", "confirm_move": "Flytt", "confirm_fork": "Forgren", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Flytt innlegg", "post_moved": "Innlegg flyttet!", "fork_topic": "Forgren Emne", - "topic_will_be_moved_to": "Dette emnet vil bli flyttet til kategorien", "fork_topic_instruction": "Trykk på innleggene du vil forgrene", "fork_no_pids": "Ingen innlegg valgt!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/nl/admin/appearance/customise.json b/public/language/nl/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/nl/admin/appearance/customise.json +++ b/public/language/nl/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/nl/admin/development/info.json b/public/language/nl/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/nl/admin/development/info.json +++ b/public/language/nl/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/nl/admin/general/languages.json b/public/language/nl/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/nl/admin/general/languages.json +++ b/public/language/nl/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/nl/admin/manage/categories.json b/public/language/nl/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/nl/admin/manage/categories.json +++ b/public/language/nl/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/nl/admin/manage/users.json b/public/language/nl/admin/manage/users.json index f1651a814b..9c75b17473 100644 --- a/public/language/nl/admin/manage/users.json +++ b/public/language/nl/admin/manage/users.json @@ -85,7 +85,7 @@ "alerts.error-x": "Error

%1

", "alerts.create-success": "User created!", - "alerts.prompt-email": "Email: ", + "alerts.prompt-email": "E-mail:", "alerts.email-sent-to": "An invitation email has been sent to %1", "alerts.x-users-found": "%1 user(s) found! Search took %2 ms." } \ No newline at end of file diff --git a/public/language/nl/admin/settings/user.json b/public/language/nl/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/nl/admin/settings/user.json +++ b/public/language/nl/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/nl/email.json b/public/language/nl/email.json index d94f164e9e..fe218e778a 100644 --- a/public/language/nl/email.json +++ b/public/language/nl/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "Deze notificatie is door ons verzonden vanwege gebruikersinstellingen voor abonnementen en berichten.", "test.text1": "Dit is een testbericht om te verifiëren dat NodeBB de e-mailberichtservice correct heeft opgezet.", "unsub.cta": "Klik hier om deze instellingen te wijzigen", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "U bent verbannen van %1", + "banned.text1": "De gebruiker %1 is verbannen van %2.", + "banned.text2": "Deze verbanning duurt tot %1.", + "banned.text3": "U bent verbannen om de volgende reden:", "closing": "Bedankt!" } \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index 588d2d3a1b..fd6097c893 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Ongeldige data", + "invalid-json": "Ongeldige JSON", "not-logged-in": "Het lijkt erop dat je niet ingelogd bent.", "account-locked": "Dit account is tijdelijk vergrendeld", "search-requires-login": "Zoeken vereist een account - meld je aan of registreer je om te zoeken.", @@ -12,6 +13,7 @@ "invalid-title": "Ongeldige titel!", "invalid-user-data": "Ongeldige gebruikersgegevens", "invalid-password": "Ongeldig wachtwoord", + "invalid-login-credentials": "Ongeldige aanmeldingsreferenties", "invalid-username-or-password": "Geef zowel een gebruikersnaam als wachtwoord op", "invalid-search-term": "Ongeldig zoekterm", "csrf-invalid": "We konden u niet aanmelden, waarschijnlijk door een verlopen sessie. Probeer het a.u.b. nogmaals.", @@ -30,7 +32,7 @@ "password-too-long": "Wachtwoord is te lang", "user-banned": "Gebruiker verbannen", "user-banned-reason": "Sorry, dit account is verbannen (Reden: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "Sorry, dit account is verbannen tot %1 (Reden: %2)", "user-too-new": "Helaas, het is een vereiste om %1 seconde(n) te wachten voordat het eerste bericht geplaatst kan worden.", "blacklisted-ip": "Sorry, uw IP-adres is verbannen uit deze community. Als u meent dat dit onterecht is, neem dan contact op met een beheerder.", "ban-expiry-missing": "Geef een einddatum op voor deze ban.", @@ -99,13 +101,13 @@ "invalid-file": "Ongeldig bestand", "uploads-are-disabled": "Uploads zijn uitgeschakeld", "signature-too-long": "Sorry, je onderschrift kan niet langer zijn dan %1 karakter(s).", - "about-me-too-long": "Sorry, je beschrijving kan niet langer zijn da %1 karakter(s).", + "about-me-too-long": "Sorry, je beschrijving kan niet langer zijn dan %1 karakter(s).", "cant-chat-with-yourself": "Het is niet mogelijk om met jezelf een chatgesprek te houden.", "chat-restricted": "Deze gebruiker heeft beperkingen aan de chatfunctie opgelegd waardoor deze eerst iemand moet volgen voordat deze persoon een nieuwe chat mag initiëren.", "chat-disabled": "Chat systeem uitgeschakeld", "too-many-messages": "Je hebt in korte tijd veel berichten verstuurd, als je even wacht mag je weer berichten sturen.", "invalid-chat-message": "Ongeldig bericht", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "Chat berichten kunnen niet groter zijn dan %1 karakters.", "cant-edit-chat-message": "Het is niet toegestaan om dit bericht aan te passen", "cant-remove-last-user": "Je kan de laatste gebruiker niet verwijderen", "cant-delete-chat-message": "Het is niet toegestaan om dit bericht te verwijderen", diff --git a/public/language/nl/global.json b/public/language/nl/global.json index f18dccae8d..c4e2d0e596 100644 --- a/public/language/nl/global.json +++ b/public/language/nl/global.json @@ -103,5 +103,7 @@ "cookies.message": "Deze website gebruikt cookies om je ervan te verzekeren dat je de beste ervaring krijgt tijdens het gebruik van onze website.", "cookies.accept": "Begrepen", "cookies.learn_more": "Meer", - "edited": "Bewerkt" + "edited": "Bewerkt", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/nl/language.json b/public/language/nl/language.json index c8fadd1797..5490106fc4 100644 --- a/public/language/nl/language.json +++ b/public/language/nl/language.json @@ -1,5 +1,5 @@ { - "name": "Engels (Verenigd Koninkrijk/Canada)", + "name": "Nederlands", "code": "nl", "dir": "ltr" } \ No newline at end of file diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index aac1dd231b..7b15303b70 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3 maanden", "chat.delete_message_confirm": "Weet je zeker dat je dit bericht wilt verwijderen?", "chat.add-users-to-room": "Voeg gebruikers toe aan deze chat room", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Deze gebruiker heeft de status op Niet Storen (DnD, Do not disturb) gezet. Wil je nog steeds een chat starten met deze persoon?", "composer.compose": "Samenstellen", "composer.show_preview": "Voorbeeldweergave", "composer.hide_preview": "Verberg voorbeeld", diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index 64441b8b54..23e86a67b0 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -28,7 +28,7 @@ "chats": "Chats", "chat": "Chatten met %1", "flags": "Markeringen", - "flag-details": "Flag %1 Details", + "flag-details": "Markering %1 details", "account/edit": "\"%1\" aanpassen", "account/edit/password": "Wachtwoord van \"%1\" aanpassen", "account/edit/username": "Gebruikersnaam van \"%1\" aanpassen", diff --git a/public/language/nl/topic.json b/public/language/nl/topic.json index 1e0be1b1c3..06b2e7f58a 100644 --- a/public/language/nl/topic.json +++ b/public/language/nl/topic.json @@ -14,6 +14,7 @@ "quote": "Citeren", "reply": "Reageren", "replies_to_this_post": "%1 Antwoorden", + "one_reply_to_this_post": "1 Antwoord", "last_reply_time": "Laatste antwoord", "reply-as-topic": "Reageren als onderwerp", "guest-login-reply": "Aanmelden om te reageren", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Onderwerp openen", "thread_tools.move": "Onderwerp verplaatsen", "thread_tools.move_all": "Verplaats alles", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Onderwerp afsplitsen", "thread_tools.delete": "Onderwerp verwijderen", "thread_tools.delete-posts": "Verwijder berichten", @@ -71,7 +73,6 @@ "post_restore_confirm": "Is het de bedoeling dit bericht te herstellen?", "post_purge_confirm": "Is het absoluut zeker dat dit bericht volledig verwijderd kan worden?", "load_categories": "Categorieën laden", - "disabled_categories_note": "Uitgeschakelde Categorieën zijn grijs", "confirm_move": "Verplaatsen", "confirm_fork": "Splits", "bookmark": "Favoriet", @@ -83,7 +84,6 @@ "move_post": "Bericht verplaatsen", "post_moved": "Bericht verplaatst!", "fork_topic": "Afgesplitst onderwerp ", - "topic_will_be_moved_to": "Dit onderwerp zal naar de categorie verplaatst worden", "fork_topic_instruction": "Klik op de berichten die afgesplitst moeten worden", "fork_no_pids": "Geen berichten geselecteerd!", "fork_pid_count": "%1 bericht(en) geselecteerd", diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 12b92dc7dd..216a26f855 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "Helaas, de gewenste gebruikersnaam is al door iemand in gebruik genomen dus vandaar een kleine aanpassing naar %1 doorgevoerd", "password_same_as_username": "Je wachtwoord is hetzelfde als je gebruikersnaam. Kies een ander wachtwoord.", "password_same_as_email": "Je wachtwoord is hetzelfde als je email, kies alsjeblieft een ander wachtwoord.", - "weak_password": "Weak password.", + "weak_password": "Zwak wachtwoord.", "upload_picture": "Upload afbeelding", "upload_a_picture": "Upload een afbeelding", "remove_uploaded_picture": "Verwijder gëuploade foto", @@ -132,5 +132,5 @@ "info.email-history": "Email geschiedenis", "info.moderation-note": "Moderatie notitie", "info.moderation-note.success": "Moderatie notitie opgeslagen", - "info.moderation-note.add": "Add note" + "info.moderation-note.add": "Notitie toevoegen" } \ No newline at end of file diff --git a/public/language/pl/admin/advanced/database.json b/public/language/pl/admin/advanced/database.json index dd365e5b0b..b35c3abe2e 100644 --- a/public/language/pl/admin/advanced/database.json +++ b/public/language/pl/admin/advanced/database.json @@ -7,7 +7,7 @@ "mongo": "Mongo", "mongo.version": "Wersja MongoDB", - "mongo.storage-engine": "Storage Engine", + "mongo.storage-engine": "Silnik Magazynowania", "mongo.collections": "Kolekcje", "mongo.objects": "Obiekty", "mongo.avg-object-size": "Przybliżony rozmiar obiektu", @@ -15,7 +15,7 @@ "mongo.storage-size": "Rozmiar pamięci", "mongo.index-size": "Rozmiar indeksu", "mongo.file-size": "Rozmiar pliku", - "mongo.resident-memory": "Resident Memory", + "mongo.resident-memory": "Przynależna Pamięć", "mongo.virtual-memory": "Pamięc wirtualna", "mongo.mapped-memory": "Pamięc zmapowana", "mongo.raw-info": "Surowe informacje MongoDB", @@ -29,8 +29,8 @@ "redis.memory-frag-ratio": "Proporcja fragmentacji pamięci", "redis.total-connections-recieved": "Otrzymanych połączeń", "redis.total-commands-processed": "Przetworzonych połączeń", - "redis.iops": "Instantaneous Ops. Per Second", - "redis.keyspace-hits": "Keyspace Hits", - "redis.keyspace-misses": "Keyspace Misses", + "redis.iops": "Natychmiastowe Operacje Na Sekundę", + "redis.keyspace-hits": "Trafienia Kluczy", + "redis.keyspace-misses": "Nietrafione Klucze", "redis.raw-info": "Surowe informacje Redis" } \ No newline at end of file diff --git a/public/language/pl/admin/appearance/customise.json b/public/language/pl/admin/appearance/customise.json index 22e1338056..99dc3569f7 100644 --- a/public/language/pl/admin/appearance/customise.json +++ b/public/language/pl/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Własny nagłówek", "custom-header.description": "Wpisz tutaj kod HTML (JavaScript, tagi <meta>) który ma być dołączony do sekcji <head> w szablonie forum.", - "custom-header.enable": "Włącz własny nagłówek" + "custom-header.enable": "Włącz własny nagłówek", + + "custom-css.livereload": "Włącz żywe przeładowanie", + "custom-css.livereload.description": "Włącz to, aby zmusić wszystkie sesje do odświeżenia na każdym urządzeniu na twoim koncie gdziekolwiek klikniesz zapisz." } \ No newline at end of file diff --git a/public/language/pl/admin/development/info.json b/public/language/pl/admin/development/info.json index d63f50a900..c018cfdf8c 100644 --- a/public/language/pl/admin/development/info.json +++ b/public/language/pl/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Informacja - Jesteś na %1:%2", + "nodes-responded": "%1 nodes odpowiedziały w ciągu %2ms!", "host": "Host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "pamięć", "load": "obciążenie", "uptime": "uptime", diff --git a/public/language/pl/admin/extend/plugins.json b/public/language/pl/admin/extend/plugins.json index 6ac7776536..ee9d5675b7 100644 --- a/public/language/pl/admin/extend/plugins.json +++ b/public/language/pl/admin/extend/plugins.json @@ -14,8 +14,8 @@ "dev-interested": "Zainteresowany pisanie pluginów do NodeBB??", "docs-info": "Pełna dokumentacje dotycząca pisania pluginów znajduje się tutaj NodeBB Docs Portal.", - "order.description": "Certain plugins work ideally when they are initialised before/after other plugins.", - "order.explanation": "Plugins load in the order specified here, from top to bottom", + "order.description": "Pewne pluginy działają idealnie kiedy są zainicjalizowane przed/po innymi pluginami.", + "order.explanation": "Pluginy ładują się tutaj w określonej kolejności, od góry do dołu.", "plugin-item.themes": "Style", "plugin-item.deactivate": "Dezaktywować", @@ -28,7 +28,7 @@ "plugin-item.upgrade": "Zaktualizuj", "plugin-item.more-info": "Po więcej informacji:", "plugin-item.unknown": "Nieznane", - "plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.", + "plugin-item.unknown-explanation": "Stan tego pluginu nie może być ustalony, prawdopodobnie z powodu błędu konfiguracji błędów.", "alert.enabled": "Plugin Włączony", "alert.disabled": "Plugin Wyłączony", @@ -40,8 +40,8 @@ "alert.upgrade-success": "Proszę przeładować NodeBB, aby poprawnie działał plugin", "alert.install-success": "Plugin pomyślnie zainstalowany, proszę aktywować go.", "alert.uninstall-success": "Plugin został pomyślnie zdezaktywowany oraz odinstalowany.", - "alert.suggest-error": "

NodeBB could not reach the package manager, proceed with installation of latest version?

Server returned (%1): %2
", - "alert.package-manager-unreachable": "

NodeBB could not reach the package manager, an upgrade is not suggested at this time.

", - "alert.incompatible": "

Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.

", + "alert.suggest-error": "

NodeBB nie może dostać się do menedżera pakietów, kontynuować instalacji ostatniej wersji?

Serwer zwrócił (%1): %2
", + "alert.package-manager-unreachable": "

NodeBB nie może dostać się do menedżera pakietów, aktualizacja nie jest sugerowana w tym momencie.

", + "alert.incompatible": "

Twoja wersja NodeBB (v%1) jest usuwana w celu uaktualnienia do v%2 tego pluginu. Proszę zaktualizuj twoje NodeBB jeżeli chcesz zainstalować nowszą wersję tego pluginu.

", "alert.possibly-incompatible": "

No Compatibility Information Found

This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.

In the event that NodeBB cannot boot properly:

$ ./nodebb reset plugin=\"%1\"

Continue installation of latest version of this plugin?

" } diff --git a/public/language/pl/admin/general/languages.json b/public/language/pl/admin/general/languages.json index d8779b4524..5f90036f0a 100644 --- a/public/language/pl/admin/general/languages.json +++ b/public/language/pl/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Ustawienia Językowe", "description": "Domyślnym językiem określa ustawienia języka dla wszystkich użytkowników, którzy odwiedzają forum.
Użytkownicy mogą zmienić domyślny język, w ustawieniach konta.", - "default-language": "Domyślny Język" + "default-language": "Domyślny Język", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/pl/admin/manage/categories.json b/public/language/pl/admin/manage/categories.json index 4eb8a57259..00699d30ac 100644 --- a/public/language/pl/admin/manage/categories.json +++ b/public/language/pl/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Niestandardowa klasa", "num-recent-replies": "# z Ostatnich Odpowiedzi", "ext-link": "Zewnętrzny Link", + "is-section": "Treat this category as a section", "upload-image": "Prześlij Obrazek", "delete-image": "Usunąć", "category-image": "Obrazek Kategorii", diff --git a/public/language/pl/admin/settings/user.json b/public/language/pl/admin/settings/user.json index ca27eca846..f9383ab222 100644 --- a/public/language/pl/admin/settings/user.json +++ b/public/language/pl/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maksymalnie ilość zaproszeń przez użytkownika", "max-invites": "Maksymalnie ilość zaproszeń przez użytkownika", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/pl/error.json b/public/language/pl/error.json index 9e1bcf4ae4..0f976f0a18 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Błędne Dane", + "invalid-json": "Invalid JSON", "not-logged-in": "Nie jesteś zalogowany/a.", "account-locked": "Twoje konto zostało tymczasowo zablokowane", "search-requires-login": "Wyszukiwanie wymaga konta - zaloguj się lub zarejestruj.", @@ -12,6 +13,7 @@ "invalid-title": "Błędny tytuł!", "invalid-user-data": "Błędne Dane Użytkownika", "invalid-password": "Błędne Hasło", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Proszę podać nazwę użytkownika i hasło", "invalid-search-term": "Błędne wyszukiwane wyrażenie", "csrf-invalid": "Nie mogliśmy Cię zalogować. Zapewne przyczyną jest wygasła sesja. Proszę spróbować ponownie.", diff --git a/public/language/pl/global.json b/public/language/pl/global.json index 0117c61c94..14dc633e53 100644 --- a/public/language/pl/global.json +++ b/public/language/pl/global.json @@ -103,5 +103,7 @@ "cookies.message": "Ta strona używa plików cookies, by zapewnić Ci najlepsze działanie naszej strony.", "cookies.accept": "Mam to!", "cookies.learn_more": "Dowiedz się więcej", - "edited": "Edytowany" + "edited": "Edytowany", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json index a51f362e60..59b297af7c 100644 --- a/public/language/pl/groups.json +++ b/public/language/pl/groups.json @@ -27,7 +27,7 @@ "details.disableJoinRequests": "Wyłączono prośbę o dołączenie", "details.grant": "Nadaj/Cofnij prawa Właściciela", "details.kick": "Wykop", - "details.kick_confirm": "Are you sure you want to remove this member from the group?", + "details.kick_confirm": "Jesteś pewny, że chcesz wyrzucić tego użytkownika z grupy?", "details.owner_options": "Administracja grupy", "details.group_name": "Nazwa grupy", "details.member_count": "Liczba Członków", diff --git a/public/language/pl/modules.json b/public/language/pl/modules.json index 1afc9a68da..222a9ef591 100644 --- a/public/language/pl/modules.json +++ b/public/language/pl/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3 miesiące", "chat.delete_message_confirm": "Jesteś pewny, że chcesz usunąć tą wiadomość?", "chat.add-users-to-room": "Dodaj użytkownika do pokoju czatu", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Ten użytkownik ustawił swój status na \"nie przeszkadzać\". Czy wciąż chcesz z nim rozmawiać?", "composer.compose": "Twórz", "composer.show_preview": "Pokaż Podgląd", "composer.hide_preview": "Ukryj Podgląd", diff --git a/public/language/pl/notifications.json b/public/language/pl/notifications.json index 77a4567ff8..9d1d4d7882 100644 --- a/public/language/pl/notifications.json +++ b/public/language/pl/notifications.json @@ -10,15 +10,15 @@ "return_to": "Wróć do %1", "new_notification": "Nowe powiadomienie", "you_have_unread_notifications": "Masz nieprzeczytane powiadomienia.", - "all": "All", + "all": "Wszystko", "topics": "Topics", - "replies": "Replies", - "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "replies": "Odpowiedzi", + "chat": "Rozmowy", + "follows": "Obserwowani", + "upvote": "Głosy na tak", + "new-flags": "Nowe flagi", + "my-flags": "Flagi przypisane mnie", + "bans": "Bany", "new_message_from": "Nowa wiadomość od %1", "upvoted_your_post_in": "%1 zagłosował na Twój post w %2", "upvoted_your_post_in_dual": "%1 oraz%2 za na twój post w %3.", diff --git a/public/language/pl/topic.json b/public/language/pl/topic.json index 9a026a1c11..515231700d 100644 --- a/public/language/pl/topic.json +++ b/public/language/pl/topic.json @@ -14,6 +14,7 @@ "quote": "Cytuj", "reply": "Odpowiedz", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Odpowiedz na temat", "guest-login-reply": "Zaloguj się, aby odpowiedzieć.", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Odblokuj Temat", "thread_tools.move": "Przenieś Temat", "thread_tools.move_all": "Przenieś wszystko", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Skopiuj Temat", "thread_tools.delete": "Usuń Temat", "thread_tools.delete-posts": "Usuń posty", @@ -71,7 +73,6 @@ "post_restore_confirm": "Na pewno chcesz przywrócić ten post?", "post_purge_confirm": "Jesteś pewien, że chcesz wyczyścić ten post?", "load_categories": "Ładowanie kategorii", - "disabled_categories_note": "Zablokowane kategorie zostały wyszarzone.", "confirm_move": "Przenieś", "confirm_fork": "Skopiuj", "bookmark": "Zakładka", @@ -83,7 +84,6 @@ "move_post": "Przenieś post", "post_moved": "Post przeniesiony!", "fork_topic": "Skopiuj Temat", - "topic_will_be_moved_to": "Ten temat zostanie przeniesiony do kategorii", "fork_topic_instruction": "Zaznacz posty, które chcesz sklonować", "fork_no_pids": "Nie zaznaczyłeś żadnych postów!", "fork_pid_count": "wybrano %1 post(-ów)", diff --git a/public/language/pt-BR/admin/appearance/customise.json b/public/language/pt-BR/admin/appearance/customise.json index 5a88e5915b..b09feb1da0 100644 --- a/public/language/pt-BR/admin/appearance/customise.json +++ b/public/language/pt-BR/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Cabeçalho Personalizado", "custom-header.description": "Adicione HTML pessoal aqui (ex. JavaScript, Meta Tags, Tags, etc), os quais serão acrescentados ao final da seção <head> do markup do seu fórum.", - "custom-header.enable": "Ligar o Cabeçalho Personalizado" + "custom-header.enable": "Ligar o Cabeçalho Personalizado", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/pt-BR/admin/development/info.json b/public/language/pt-BR/admin/development/info.json index 6b16f9a46a..4d754e5d85 100644 --- a/public/language/pt-BR/admin/development/info.json +++ b/public/language/pt-BR/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Informação - Você está em %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "carregar", "uptime": "tempo rodando", diff --git a/public/language/pt-BR/admin/general/languages.json b/public/language/pt-BR/admin/general/languages.json index 25b203c2f2..fe5213d0dc 100644 --- a/public/language/pt-BR/admin/general/languages.json +++ b/public/language/pt-BR/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Configurações de Idioma", "description": "O idioma padrão determina as configurações de idioma para todos os usuários que estiverem visitando o seu fórum.
Usuários individuais podem sobrepor o idioma padrão em sua página de configurações de conta.", - "default-language": "Idioma Padrão" + "default-language": "Idioma Padrão", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/pt-BR/admin/manage/categories.json b/public/language/pt-BR/admin/manage/categories.json index 65d0e5bc81..f2f06cf835 100644 --- a/public/language/pt-BR/admin/manage/categories.json +++ b/public/language/pt-BR/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Classe Personalizada", "num-recent-replies": "# de Respostas Recentes", "ext-link": "Link Externo", + "is-section": "Treat this category as a section", "upload-image": "Fazer upload de Imagem", "delete-image": "Remover", "category-image": "Imagem da Categoria", diff --git a/public/language/pt-BR/admin/settings/user.json b/public/language/pt-BR/admin/settings/user.json index 9ccdd6baa5..a849488cc9 100644 --- a/public/language/pt-BR/admin/settings/user.json +++ b/public/language/pt-BR/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Máximo de Convites por Usuário", "max-invites": "Máximo de Convites por Usuário", "max-invites-help": "0 para nenhuma restrição. Administradores tem convites infinitos
Apenas aplicável para \"Apenas Convite\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Tamanho Mínimo do Nome de Usuário", "max-username-length": "Tamanho Máximo do Nome de Usuário", "min-password-length": "Tamanho Mínimo da Senha", diff --git a/public/language/pt-BR/error.json b/public/language/pt-BR/error.json index ba2053e547..0463e2ad5e 100644 --- a/public/language/pt-BR/error.json +++ b/public/language/pt-BR/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Dados Inválidos", + "invalid-json": "Invalid JSON", "not-logged-in": "Você não parece estar logado.", "account-locked": "Sua conta foi temporariamente bloqueada ", "search-requires-login": "É necessário ter uma conta para pesquisar - por favor efetue o login ou cadastre-se.", @@ -12,6 +13,7 @@ "invalid-title": "Título inválido!", "invalid-user-data": "Dados de Usuário Inválidos", "invalid-password": "Senha Inválida", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Por favor especifique ambos nome de usuário e senha", "invalid-search-term": "Termo de pesquisa inválido", "csrf-invalid": "Nós não fomos capazes de logá-lo, provavelmente devido à uma sessão expirada. Por favor tente novamente.", diff --git a/public/language/pt-BR/global.json b/public/language/pt-BR/global.json index 914a11e049..f173968642 100644 --- a/public/language/pt-BR/global.json +++ b/public/language/pt-BR/global.json @@ -103,5 +103,7 @@ "cookies.message": "Este website utiliza cookies para assegrar que você tenha a melhor experiência em nosso site.", "cookies.accept": "Entendi!", "cookies.learn_more": "Saber Mais", - "edited": "Editado" + "edited": "Editado", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/pt-BR/topic.json b/public/language/pt-BR/topic.json index 483c769ade..8e6ed0aa7e 100644 --- a/public/language/pt-BR/topic.json +++ b/public/language/pt-BR/topic.json @@ -14,6 +14,7 @@ "quote": "Citar", "reply": "Responder", "replies_to_this_post": "%1 Respostas", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Última resposta", "reply-as-topic": "Responder como tópico", "guest-login-reply": "Entre para responder", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Destrancar Tópico", "thread_tools.move": "Mover Tópico", "thread_tools.move_all": "Mover Tudo", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Ramificar Tópico", "thread_tools.delete": "Deletar Tópico", "thread_tools.delete-posts": "Deletar Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Tem certeza que deseja restaurar este post?", "post_purge_confirm": "Tem certeza que deseja expurgar este post?", "load_categories": "Carregando Categorias", - "disabled_categories_note": "Categorias desabilitadas estão em cinza", "confirm_move": "Mover", "confirm_fork": "Ramificar", "bookmark": "Favorito", @@ -83,7 +84,6 @@ "move_post": "Mover Post", "post_moved": "Post movido!", "fork_topic": "Ramificar Tópico", - "topic_will_be_moved_to": "Este tópico será movido para a categoria", "fork_topic_instruction": "Clique nos posts que você quer ramificar", "fork_no_pids": "Nenhum post selecionado!", "fork_pid_count": "%1 post(s) selecionado(s)", diff --git a/public/language/pt-PT/admin/appearance/customise.json b/public/language/pt-PT/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/pt-PT/admin/appearance/customise.json +++ b/public/language/pt-PT/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/pt-PT/admin/development/info.json b/public/language/pt-PT/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/pt-PT/admin/development/info.json +++ b/public/language/pt-PT/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/pt-PT/admin/general/languages.json b/public/language/pt-PT/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/pt-PT/admin/general/languages.json +++ b/public/language/pt-PT/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/pt-PT/admin/manage/categories.json b/public/language/pt-PT/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/pt-PT/admin/manage/categories.json +++ b/public/language/pt-PT/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/pt-PT/admin/settings/user.json b/public/language/pt-PT/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/pt-PT/admin/settings/user.json +++ b/public/language/pt-PT/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/pt-PT/error.json b/public/language/pt-PT/error.json index d87e005288..07fa77e963 100644 --- a/public/language/pt-PT/error.json +++ b/public/language/pt-PT/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Dados inválidos", + "invalid-json": "Invalid JSON", "not-logged-in": "Não tem sessão iniciada.", "account-locked": "A sua conta foi bloqueada temporariamente", "search-requires-login": "A pesquisa requer uma conta de utilizador - For favor inicie sessão ou crie uma conta.", @@ -12,6 +13,7 @@ "invalid-title": "Título inválido!", "invalid-user-data": "Dados de utilizador inválidos", "invalid-password": "Senha inválida", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Por favor especificar um nome de utilizador e senha", "invalid-search-term": "Termo de pesquisa inválido", "csrf-invalid": "Não conseguimos efetuar o teu login, provavelmente devido a uma sessão que já expirou. Por favor, tenta novamente", diff --git a/public/language/pt-PT/global.json b/public/language/pt-PT/global.json index 42e887b6aa..8a40b20a00 100644 --- a/public/language/pt-PT/global.json +++ b/public/language/pt-PT/global.json @@ -103,5 +103,7 @@ "cookies.message": "Este website utiliza cookies para assegurar que tens a melhor experiência no nosso website.", "cookies.accept": "Apontado!", "cookies.learn_more": "Aprende mais", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/pt-PT/notifications.json b/public/language/pt-PT/notifications.json index 6e7c61c457..cc059ae2b4 100644 --- a/public/language/pt-PT/notifications.json +++ b/public/language/pt-PT/notifications.json @@ -10,15 +10,15 @@ "return_to": "Voltar a %1", "new_notification": "Nova notificação", "you_have_unread_notifications": "Tens notificações por ler.", - "all": "All", + "all": "Tudo", "topics": "Topics", - "replies": "Replies", - "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "replies": "Respostas", + "chat": "Chat", + "follows": "Novos seguidores", + "upvote": "Votos positivos", + "new-flags": "Novas sinalizações", + "my-flags": "Sinalizações para mim", + "bans": "Banimentos", "new_message_from": "Nova mensagem de %1", "upvoted_your_post_in": "%1 votou de forma favorável na tua publicação em %2.", "upvoted_your_post_in_dual": "%1 e %2 votaram favoravelmente à tua publicação em %3.", diff --git a/public/language/pt-PT/topic.json b/public/language/pt-PT/topic.json index 6622afb644..9647b731c5 100644 --- a/public/language/pt-PT/topic.json +++ b/public/language/pt-PT/topic.json @@ -14,6 +14,7 @@ "quote": "Citar", "reply": "Responder", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Responder com um tópico", "guest-login-reply": "Faz login para responder", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Desbloquear tópico", "thread_tools.move": "Mover tópico", "thread_tools.move_all": "Mover todos", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Clonar tópico", "thread_tools.delete": "Eliminar tópico", "thread_tools.delete-posts": "Apagar publicações", @@ -71,7 +73,6 @@ "post_restore_confirm": "Tens a certeza que desejas restaurar esta publicação?", "post_purge_confirm": "Tens a certeza que desejas purgar esta publicação?", "load_categories": "Carregando Categorias", - "disabled_categories_note": "Desativar Categorias que estão a cinzento", "confirm_move": "Mover", "confirm_fork": "Clonar", "bookmark": "Marcador", @@ -83,7 +84,6 @@ "move_post": "Mover publicação", "post_moved": "Publicação movida!", "fork_topic": "Clonar tópico", - "topic_will_be_moved_to": "Este tópico será movido para a categoria", "fork_topic_instruction": "Carrega nas publicações que queres clonar", "fork_no_pids": "Sem publicações selecionadas!", "fork_pid_count": "%1 publicação(ões) selecionada(s)", diff --git a/public/language/ro/admin/appearance/customise.json b/public/language/ro/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/ro/admin/appearance/customise.json +++ b/public/language/ro/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/ro/admin/development/info.json b/public/language/ro/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/ro/admin/development/info.json +++ b/public/language/ro/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/ro/admin/general/languages.json b/public/language/ro/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/ro/admin/general/languages.json +++ b/public/language/ro/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/ro/admin/manage/categories.json b/public/language/ro/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/ro/admin/manage/categories.json +++ b/public/language/ro/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/ro/admin/settings/user.json b/public/language/ro/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/ro/admin/settings/user.json +++ b/public/language/ro/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/ro/error.json b/public/language/ro/error.json index 82112f70c0..6a7ecea6f3 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Date invalide", + "invalid-json": "Invalid JSON", "not-logged-in": "Se pare ca nu ești logat.", "account-locked": "Contul tău a fost blocat temporar", "search-requires-login": "Pentru a cauta ai nevoie de un cont. Logheaza-te sau autentifica-te.", @@ -12,6 +13,7 @@ "invalid-title": "Titlu invalid!", "invalid-user-data": "Date utilizator invalide", "invalid-password": "Parolă Invalidă", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Te rugăm să specifici atât un nume de utilizator cât si o parolă", "invalid-search-term": "Cuvânt de căutare invalid", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/ro/global.json b/public/language/ro/global.json index a7af755c6b..f4b463beb1 100644 --- a/public/language/ro/global.json +++ b/public/language/ro/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/ro/topic.json b/public/language/ro/topic.json index 370981d72e..36ed0ffb0f 100644 --- a/public/language/ro/topic.json +++ b/public/language/ro/topic.json @@ -14,6 +14,7 @@ "quote": "Citează", "reply": "Răspunde", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Răspunde ca subiect", "guest-login-reply": "Login pentru a răspunde", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Deschide Subiect", "thread_tools.move": "Mută Subiect", "thread_tools.move_all": "Mută-le pe toate", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Bifurcă Subiect", "thread_tools.delete": "Șterge Subiect", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Esti sigur că vrei să restaurezi acest mesaj?", "post_purge_confirm": "Ești sigur că vrei să cureți acest mesaj?", "load_categories": "Se Încarcă Categoriile", - "disabled_categories_note": "Categoriile dezactivate sunt decolorate cu gri", "confirm_move": "Mută", "confirm_fork": "Bifurcă", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Mută Mesaj", "post_moved": "Mesaj mutat!", "fork_topic": "Bifurcă Subiect", - "topic_will_be_moved_to": "Acest subiect va fi mutat în categoria", "fork_topic_instruction": "Apasă pe mesajele care vrei sa le bifurci", "fork_no_pids": "Nu a fost selectat nici un mesaj!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/ru/admin/appearance/customise.json b/public/language/ru/admin/appearance/customise.json index 226ce535ee..dfed0bf11c 100644 --- a/public/language/ru/admin/appearance/customise.json +++ b/public/language/ru/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/ru/admin/development/info.json b/public/language/ru/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/ru/admin/development/info.json +++ b/public/language/ru/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/ru/admin/general/languages.json b/public/language/ru/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/ru/admin/general/languages.json +++ b/public/language/ru/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/ru/admin/manage/categories.json b/public/language/ru/admin/manage/categories.json index 78519ee4f5..c88a402511 100644 --- a/public/language/ru/admin/manage/categories.json +++ b/public/language/ru/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Свой класс", "num-recent-replies": "# of Recent Replies", "ext-link": "Внешняя ссылка", + "is-section": "Treat this category as a section", "upload-image": "Загрузить изображение", "delete-image": "Удалить", "category-image": "Изображение категории", diff --git a/public/language/ru/admin/settings/user.json b/public/language/ru/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/ru/admin/settings/user.json +++ b/public/language/ru/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/ru/error.json b/public/language/ru/error.json index c1aecb322e..822af1d494 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Неверные данные", + "invalid-json": "Invalid JSON", "not-logged-in": "Вы не вошли на сайт.", "account-locked": "Учётная запись временно заблокирована", "search-requires-login": "Поиск доступен только для зарегистрированных участников. Пожалуйста, войдите или зарегистрируйтесь.", @@ -12,6 +13,7 @@ "invalid-title": "Неверный заголовок", "invalid-user-data": "Неверные пользовательские данные", "invalid-password": "Неверный пароль", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Пожалуйста, укажите имя пользователя и пароль", "invalid-search-term": "Неверный поисковой запрос", "csrf-invalid": "Нам не удалось вас найти из-за просроченной сессии. Попробуйте ещё раз.", diff --git a/public/language/ru/global.json b/public/language/ru/global.json index bafb1db8e2..5812ee446d 100644 --- a/public/language/ru/global.json +++ b/public/language/ru/global.json @@ -103,5 +103,7 @@ "cookies.message": "Этот сайт использует cookies для более удобного взаимодействия.", "cookies.accept": "Понял", "cookies.learn_more": "Подробнее", - "edited": "Отредактированный" + "edited": "Отредактированный", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index 0e16058163..81f06dd3b1 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -10,15 +10,15 @@ "return_to": "Вернуться к %1", "new_notification": "Новое уведомление", "you_have_unread_notifications": "У вас есть непрочитанные уведомления.", - "all": "All", + "all": "Все", "topics": "Topics", - "replies": "Replies", - "chat": "Chats", - "follows": "Follows", - "upvote": "Upvotes", - "new-flags": "New Flags", - "my-flags": "Flags assigned to me", - "bans": "Bans", + "replies": "Ответы", + "chat": "Чаты", + "follows": "Следят", + "upvote": "Понравилось", + "new-flags": "Новые закладки", + "my-flags": "Закладки для меня", + "bans": "Блокировки", "new_message_from": "Новое сообщение от участника %1", "upvoted_your_post_in": "Участник %1 проголосовал за ваше сообщение в %2.", "upvoted_your_post_in_dual": "Участники %1 и %2 проголосовали за ваше сообщение в %3.", diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index 45e0d731a3..9851358016 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -14,6 +14,7 @@ "quote": "Цитировать", "reply": "Ответить", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Ответить, создав новую тему", "guest-login-reply": "Необходимо авторизоваться на сайте, чтобы оставить сообщение. Пожалуйста, зарегистрируйтесь или войдите на сайт.", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Открыть тему", "thread_tools.move": "Переместить тему", "thread_tools.move_all": "Переместить всё", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Создать дополнительную ветвь дискуссии", "thread_tools.delete": "Удалить тему", "thread_tools.delete-posts": "Удалить записи", @@ -71,7 +73,6 @@ "post_restore_confirm": "Вы уверены, что хотите восстановить эту запись?", "post_purge_confirm": "Вы уверены, что хотите стереть эту запись?", "load_categories": "Загружаем сообщества", - "disabled_categories_note": "Отключённые сообщества затемнены", "confirm_move": "Перенести", "confirm_fork": "Создать дополнительную ветвь", "bookmark": "Добавить в закладки", @@ -83,7 +84,6 @@ "move_post": "Перенести запись", "post_moved": "Запись перенесена", "fork_topic": "Создать дополнительную ветвь дискуссии", - "topic_will_be_moved_to": "Эта тема будет перенесена в сообщество", "fork_topic_instruction": "Отметьте одну или несколько записей для создания дополнительной ветви дискуссии", "fork_no_pids": "Записи не отмечены", "fork_pid_count": "Отмечено %1 сообщений", diff --git a/public/language/rw/admin/appearance/customise.json b/public/language/rw/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/rw/admin/appearance/customise.json +++ b/public/language/rw/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/rw/admin/development/info.json b/public/language/rw/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/rw/admin/development/info.json +++ b/public/language/rw/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/rw/admin/general/languages.json b/public/language/rw/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/rw/admin/general/languages.json +++ b/public/language/rw/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/rw/admin/manage/categories.json b/public/language/rw/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/rw/admin/manage/categories.json +++ b/public/language/rw/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/rw/admin/settings/user.json b/public/language/rw/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/rw/admin/settings/user.json +++ b/public/language/rw/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/rw/error.json b/public/language/rw/error.json index 3e6c5cdeff..42bee55552 100644 --- a/public/language/rw/error.json +++ b/public/language/rw/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Ibyashyizwemo Ntibyemewe", + "invalid-json": "Invalid JSON", "not-logged-in": "Biragaragara ko utinjiyemo.", "account-locked": "Konte yawe yabaye ifunze", "search-requires-login": "Gushaka ikintu bisaba kuba ufite konte - Injiramo cyangwa wiyandike.", @@ -12,6 +13,7 @@ "invalid-title": "Umutwe utemewe!", "invalid-user-data": "Ibyatanzwe Ntibyemewe!", "invalid-password": "Ijambobanga Ntiryemewe", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Tanga izina ukoresha n'ijambobanga", "invalid-search-term": "Icyashatswe nticyemewe", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/rw/global.json b/public/language/rw/global.json index e35b278c53..fcba501d31 100644 --- a/public/language/rw/global.json +++ b/public/language/rw/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/rw/topic.json b/public/language/rw/topic.json index b8d25791e2..dfe0130e0f 100644 --- a/public/language/rw/topic.json +++ b/public/language/rw/topic.json @@ -14,6 +14,7 @@ "quote": "Terura", "reply": "Subiza", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Bishyireho nk'ikiganiro", "guest-login-reply": "Injiramo maze usubize", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Fungurira Ikiganiro", "thread_tools.move": "Imura Ikiganiro", "thread_tools.move_all": "Byimure Byose", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Gabanyaho ku Kiganiro", "thread_tools.delete": "Kuraho Ikiganiro", "thread_tools.delete-posts": "Siba Icyashizweho", @@ -71,7 +73,6 @@ "post_restore_confirm": "Wiringiye neza ko ushaka kugarura iki kiganiro? ", "post_purge_confirm": "Wiringiye neza ko ushaka gusibangaya iki kiganiro?", "load_categories": "Ibyiciro Biraje", - "disabled_categories_note": "Ibyiciro bitagaragazwa birasa n'ibipfutse", "confirm_move": "Imura", "confirm_fork": "Gabanyaho", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Imura Icyashyizweho", "post_moved": "Icyashizweho kirimuwe!", "fork_topic": "Gabanyaho ku Kiganiro", - "topic_will_be_moved_to": "Iki kiganiro kirimurirwa mu cyiciro", "fork_topic_instruction": "Kanda ku byashizweho ushaka kugabanyaho", "fork_no_pids": "Nta kintu wahisemo!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/sc/admin/appearance/customise.json b/public/language/sc/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/sc/admin/appearance/customise.json +++ b/public/language/sc/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/sc/admin/development/info.json b/public/language/sc/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/sc/admin/development/info.json +++ b/public/language/sc/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/sc/admin/general/languages.json b/public/language/sc/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/sc/admin/general/languages.json +++ b/public/language/sc/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/sc/admin/manage/categories.json b/public/language/sc/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/sc/admin/manage/categories.json +++ b/public/language/sc/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/sc/admin/settings/user.json b/public/language/sc/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/sc/admin/settings/user.json +++ b/public/language/sc/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 35eaf8cbc6..403c0b4aa7 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Invalid Data", + "invalid-json": "Invalid JSON", "not-logged-in": "You don't seem to be logged in.", "account-locked": "Your account has been locked temporarily", "search-requires-login": "Searching requires an account - please login or register.", @@ -12,6 +13,7 @@ "invalid-title": "Invalid title!", "invalid-user-data": "Invalid User Data", "invalid-password": "Invalid Password", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Please specify both a username and password", "invalid-search-term": "Invalid search term", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", diff --git a/public/language/sc/global.json b/public/language/sc/global.json index cd3d534dbd..26e9d5382a 100644 --- a/public/language/sc/global.json +++ b/public/language/sc/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/sc/topic.json b/public/language/sc/topic.json index 9ce7552faa..f5e4b1b376 100644 --- a/public/language/sc/topic.json +++ b/public/language/sc/topic.json @@ -14,6 +14,7 @@ "quote": "Mèntova", "reply": "Risponde", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Reply as topic", "guest-login-reply": "Log in to reply", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Isbloca Arresonada", "thread_tools.move": "Move Arresonada", "thread_tools.move_all": "Move All", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Partzi Arresonada", "thread_tools.delete": "Cantzella Arresonada", "thread_tools.delete-posts": "Delete Posts", @@ -71,7 +73,6 @@ "post_restore_confirm": "Are you sure you want to restore this post?", "post_purge_confirm": "Are you sure you want to purge this post?", "load_categories": "Carrighende Crezes", - "disabled_categories_note": "Is Crezes Disativadas sunt postas in colore de chìghine", "confirm_move": "Move", "confirm_fork": "Partzi", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Move Arresonu", "post_moved": "Post moved!", "fork_topic": "Partzi Arresonada", - "topic_will_be_moved_to": "Custa arresonada at a èssere mòvida in sa creze", "fork_topic_instruction": "Sèbera is arresonos chi boles partzire", "fork_no_pids": "Perunu arresonu seberadu!", "fork_pid_count": "%1 post(s) selected", diff --git a/public/language/sk/admin/advanced/errors.json b/public/language/sk/admin/advanced/errors.json index 02a4a0c461..c2cc92e033 100644 --- a/public/language/sk/admin/advanced/errors.json +++ b/public/language/sk/admin/advanced/errors.json @@ -1,6 +1,6 @@ { "figure-x": "Znázorniť %1", - "error-events-per-day": "%1 events per day", + "error-events-per-day": "%1 udalostí za deň", "error.404": "404 Nenájdené", "error.503": "503 Služba nie je k dispozícií", "manage-error-log": "Manage Error Log", diff --git a/public/language/sk/admin/appearance/customise.json b/public/language/sk/admin/appearance/customise.json index 8a69b45f84..1cf04a6c75 100644 --- a/public/language/sk/admin/appearance/customise.json +++ b/public/language/sk/admin/appearance/customise.json @@ -1,9 +1,12 @@ { "custom-css": "Vlastné CSS", - "custom-css.description": "Enter your own CSS declarations here, which will be applied after all other styles.", + "custom-css.description": "Zadajte svoje vlastné deklarácie CSS, ktoré budú použité po všetkých ostatných štýloch.", "custom-css.enable": "Enable Custom CSS", "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/sk/admin/development/info.json b/public/language/sk/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/sk/admin/development/info.json +++ b/public/language/sk/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/sk/admin/development/logger.json b/public/language/sk/admin/development/logger.json index 6ab9558149..1aac321d37 100644 --- a/public/language/sk/admin/development/logger.json +++ b/public/language/sk/admin/development/logger.json @@ -1,6 +1,6 @@ { - "logger-settings": "Logger Settings", - "description": "By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.", + "logger-settings": "Nastavenia protokolov", + "description": "Povolením začiarkavacích políčok budete dostávať protokoly do terminálu. Ak zadáte cestu, protokoly sa potom uložia do súboru. HTTP protokolovanie je užitočné pri zbere štatistických údajov o tom, kto, kedy a čo ľudia pristupujú na vaše fórum. Okrem zaznamenávania požiadaviek HTTP môžeme zaznamenať udalosti socket.io. Socket.io protokolovanie v kombinácii s monitorom redis-cli môže byť veľmi užitočné pre učenie NodeBB vstavby.", "explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.", "enable-http": "Enable HTTP logging", "enable-socket": "Enable socket.io event logging", diff --git a/public/language/sk/admin/extend/plugins.json b/public/language/sk/admin/extend/plugins.json index 1661a987b7..a5dae9836a 100644 --- a/public/language/sk/admin/extend/plugins.json +++ b/public/language/sk/admin/extend/plugins.json @@ -1,6 +1,6 @@ { - "installed": "Installed", - "active": "Active", + "installed": "Nainštalovaný", + "active": "Aktívny", "inactive": "Inactive", "out-of-date": "Out of Date", "none-found": "No plugins found.", diff --git a/public/language/sk/admin/extend/rewards.json b/public/language/sk/admin/extend/rewards.json index 5383a90b33..413cd9bc83 100644 --- a/public/language/sk/admin/extend/rewards.json +++ b/public/language/sk/admin/extend/rewards.json @@ -1,6 +1,6 @@ { - "rewards": "Rewards", - "condition-if-users": "If User's", + "rewards": "Odmeny", + "condition-if-users": "Ak je používateľ", "condition-is": "Is:", "condition-then": "Then:", "max-claims": "Amount of times reward is claimable", diff --git a/public/language/sk/admin/extend/widgets.json b/public/language/sk/admin/extend/widgets.json index 477bb15e56..5b2da907fe 100644 --- a/public/language/sk/admin/extend/widgets.json +++ b/public/language/sk/admin/extend/widgets.json @@ -1,6 +1,6 @@ { - "available": "Available Widgets", - "explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.", + "available": "Dostupné miniaplikácie", + "explanation": "V rozbaľovacej ponuke vyberte miniaplikáciu a potom ju presuňte do oblasti miniaplikácií šablóny.", "none-installed": "No widgets found! Activate the essential widgets plugin in the plugins control panel.", "containers.available": "Available Containers", "containers.explanation": "Drag and drop on top of any active widget", diff --git a/public/language/sk/admin/general/homepage.json b/public/language/sk/admin/general/homepage.json index 4866b8baf6..23c407427f 100644 --- a/public/language/sk/admin/general/homepage.json +++ b/public/language/sk/admin/general/homepage.json @@ -1,6 +1,6 @@ { - "home-page": "Home Page", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", + "home-page": "Domovská stránka", + "description": "Vyberte, akú stránku sa zobrazí, keď sa používatelia dostanú do koreňovej adresy URL vášho fóra.", "home-page-route": "Home Page Route", "custom-route": "Custom Route", "allow-user-home-pages": "Allow User Home Pages" diff --git a/public/language/sk/admin/general/languages.json b/public/language/sk/admin/general/languages.json index da45cade2c..d43828e793 100644 --- a/public/language/sk/admin/general/languages.json +++ b/public/language/sk/admin/general/languages.json @@ -1,5 +1,6 @@ { - "language-settings": "Language Settings", + "language-settings": "Jazykové nastavenia", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/sk/admin/general/navigation.json b/public/language/sk/admin/general/navigation.json index c4ba0d09ac..215cf9ba10 100644 --- a/public/language/sk/admin/general/navigation.json +++ b/public/language/sk/admin/general/navigation.json @@ -1,5 +1,5 @@ { - "icon": "Icon:", + "icon": "Ikona:", "change-icon": "change", "route": "Route:", "tooltip": "Tooltip:", diff --git a/public/language/sk/admin/general/social.json b/public/language/sk/admin/general/social.json index 23aedfcfaa..5f802719c0 100644 --- a/public/language/sk/admin/general/social.json +++ b/public/language/sk/admin/general/social.json @@ -1,5 +1,5 @@ { - "post-sharing": "Post Sharing", + "post-sharing": "Zdieľanie príspevkov", "info-plugins-additional": "Plugins can add additional networks for sharing posts.", "save-success": "Successfully saved Post Sharing Networks!" } \ No newline at end of file diff --git a/public/language/sk/admin/general/sounds.json b/public/language/sk/admin/general/sounds.json index 95ccbde0f1..e13810f4cb 100644 --- a/public/language/sk/admin/general/sounds.json +++ b/public/language/sk/admin/general/sounds.json @@ -1,5 +1,5 @@ { - "notifications": "Notifications", + "notifications": "Oznámenia", "chat-messages": "Chat Messages", "play-sound": "Play", "incoming-message": "Incoming Message", diff --git a/public/language/sk/admin/manage/categories.json b/public/language/sk/admin/manage/categories.json index 7e2a5ce12e..9d5330d930 100644 --- a/public/language/sk/admin/manage/categories.json +++ b/public/language/sk/admin/manage/categories.json @@ -1,5 +1,5 @@ { - "settings": "Category Settings", + "settings": "Nastavenia kategórie", "privileges": "Privileges", "name": "Category Name", @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/sk/admin/manage/groups.json b/public/language/sk/admin/manage/groups.json index c019ec9823..cc3de74004 100644 --- a/public/language/sk/admin/manage/groups.json +++ b/public/language/sk/admin/manage/groups.json @@ -1,5 +1,5 @@ { - "name": "Group Name", + "name": "Názov skupiny", "description": "Group Description", "member-count": "Member Count", "system": "System Group", diff --git a/public/language/sk/admin/manage/ip-blacklist.json b/public/language/sk/admin/manage/ip-blacklist.json index 5106434351..e7aadd2d5e 100644 --- a/public/language/sk/admin/manage/ip-blacklist.json +++ b/public/language/sk/admin/manage/ip-blacklist.json @@ -1,5 +1,5 @@ { - "lead": "Configure your IP blacklist here.", + "lead": "Nakonfigurujte si čierny zoznam IP.", "description": "Occasionally, a user account ban is not enough of a deterrant. Other times, restricting access to the forum to a specific IP or a range of IPs is the best way to protect a forum. In these scenarios, you can add troublesome IP addresses or entire CIDR blocks to this blacklist, and they will be prevented from logging in to or registering a new account.", "active-rules": "Active Rules", "validate": "Validate Blacklist", diff --git a/public/language/sk/admin/manage/registration.json b/public/language/sk/admin/manage/registration.json index f51b4d56e6..04fc0f204e 100644 --- a/public/language/sk/admin/manage/registration.json +++ b/public/language/sk/admin/manage/registration.json @@ -1,5 +1,5 @@ { - "queue": "Queue", + "queue": "Fronta", "description": "There are no users in the registration queue.
To enable this feature, go to Settings → User → User Registration and set Registration Type to \"Admin Approval\".", "list.name": "Name", diff --git a/public/language/sk/admin/manage/tags.json b/public/language/sk/admin/manage/tags.json index 775a9aed63..c0a40543f2 100644 --- a/public/language/sk/admin/manage/tags.json +++ b/public/language/sk/admin/manage/tags.json @@ -1,5 +1,5 @@ { - "none": "Your forum does not have any topics with tags yet.", + "none": "Vaše fórum zatiaľ neobsahuje žiadne témy.", "bg-color": "Background Colour", "text-color": "Text Colour", "create-modify": "Create & Modify Tags", diff --git a/public/language/sk/admin/manage/users.json b/public/language/sk/admin/manage/users.json index 1e808c70fa..24bc916cff 100644 --- a/public/language/sk/admin/manage/users.json +++ b/public/language/sk/admin/manage/users.json @@ -1,5 +1,5 @@ { - "users": "Users", + "users": "Používatelia", "edit": "Edit", "make-admin": "Make Admin", "remove-admin": "Remove Admin", diff --git a/public/language/sk/admin/menu.json b/public/language/sk/admin/menu.json index 0a1c30b63c..25dd937403 100644 --- a/public/language/sk/admin/menu.json +++ b/public/language/sk/admin/menu.json @@ -1,6 +1,6 @@ { "section-general": "Všeobecné", - "general/dashboard": "Dashboard", + "general/dashboard": "Prístrojová doska", "general/homepage": "Home Page", "general/navigation": "Navigation", "general/languages": "Languages", diff --git a/public/language/sk/admin/settings/advanced.json b/public/language/sk/admin/settings/advanced.json index 05a1929cf0..a73064e312 100644 --- a/public/language/sk/admin/settings/advanced.json +++ b/public/language/sk/admin/settings/advanced.json @@ -1,5 +1,5 @@ { - "maintenance-mode": "Maintenance Mode", + "maintenance-mode": "Režim údržby", "maintenance-mode.help": "When the forum is in maintenance mode, all requests will be redirected to a static holding page. Administrators are exempt from this redirection, and are able to access the site normally.", "maintenance-mode.message": "Maintenance Message", "headers": "Headers", diff --git a/public/language/sk/admin/settings/chat.json b/public/language/sk/admin/settings/chat.json index 0b22127341..163a472265 100644 --- a/public/language/sk/admin/settings/chat.json +++ b/public/language/sk/admin/settings/chat.json @@ -1,5 +1,5 @@ { - "chat-settings": "Chat Settings", + "chat-settings": "Nastavenia konverzácie", "disable": "Disable chat", "disable-editing": "Disable chat message editing/deletion", "disable-editing-help": "Administrators and global moderators are exempt from this restriction", diff --git a/public/language/sk/admin/settings/cookies.json b/public/language/sk/admin/settings/cookies.json index f8b0f0538b..7a7d5c2187 100644 --- a/public/language/sk/admin/settings/cookies.json +++ b/public/language/sk/admin/settings/cookies.json @@ -1,5 +1,5 @@ { - "eu-consent": "EU Consent", + "eu-consent": "Súhlas EÚ", "consent.enabled": "Enabled", "consent.message": "Notification message", "consent.acceptance": "Acceptance message", diff --git a/public/language/sk/admin/settings/email.json b/public/language/sk/admin/settings/email.json index 1e92c88490..cf3161d806 100644 --- a/public/language/sk/admin/settings/email.json +++ b/public/language/sk/admin/settings/email.json @@ -1,5 +1,5 @@ { - "email-settings": "Email Settings", + "email-settings": "Nastavenia e-mailu", "address": "Email Address", "address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.", "from": "From Name", diff --git a/public/language/sk/admin/settings/general.json b/public/language/sk/admin/settings/general.json index 3f2814bd88..e20d726f79 100644 --- a/public/language/sk/admin/settings/general.json +++ b/public/language/sk/admin/settings/general.json @@ -1,5 +1,5 @@ { - "site-settings": "Site Settings", + "site-settings": "Nastavenia lokality", "title": "Site Title", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", diff --git a/public/language/sk/admin/settings/group.json b/public/language/sk/admin/settings/group.json index 1ae88c9cf5..be6e3cd529 100644 --- a/public/language/sk/admin/settings/group.json +++ b/public/language/sk/admin/settings/group.json @@ -1,5 +1,5 @@ { - "general": "General", + "general": "Všeobecné", "private-groups": "Private Groups", "private-groups.help": "If enabled, joining of groups requires the approval of the group owner (Default: enabled)", "private-groups.warning": "Beware! If this option is disabled and you have private groups, they automatically become public.", diff --git a/public/language/sk/admin/settings/notifications.json b/public/language/sk/admin/settings/notifications.json index 4eff7f341a..c2eb1adbeb 100644 --- a/public/language/sk/admin/settings/notifications.json +++ b/public/language/sk/admin/settings/notifications.json @@ -1,5 +1,5 @@ { - "notifications": "Notifications", + "notifications": "Oznámenia", "welcome-notification": "Welcome Notification", "welcome-notification-link": "Welcome Notification Link" } \ No newline at end of file diff --git a/public/language/sk/admin/settings/pagination.json b/public/language/sk/admin/settings/pagination.json index 27d71b4de5..776160734f 100644 --- a/public/language/sk/admin/settings/pagination.json +++ b/public/language/sk/admin/settings/pagination.json @@ -1,5 +1,5 @@ { - "pagination": "Pagination Settings", + "pagination": "Nastavenia stránkovania", "enable": "Paginate topics and posts instead of using infinite scroll.", "topics": "Topic Pagination", "posts-per-page": "Posts per Page", diff --git a/public/language/sk/admin/settings/post.json b/public/language/sk/admin/settings/post.json index 90c27cc606..573d48fe05 100644 --- a/public/language/sk/admin/settings/post.json +++ b/public/language/sk/admin/settings/post.json @@ -1,5 +1,5 @@ { - "sorting": "Post Sorting", + "sorting": "Zoraďovanie príspevkov", "sorting.post-default": "Default Post Sorting", "sorting.oldest-to-newest": "Oldest to Newest", "sorting.newest-to-oldest": "Newest to Oldest", diff --git a/public/language/sk/admin/settings/sockets.json b/public/language/sk/admin/settings/sockets.json index d04ee42fcf..6bfaf49545 100644 --- a/public/language/sk/admin/settings/sockets.json +++ b/public/language/sk/admin/settings/sockets.json @@ -1,5 +1,5 @@ { - "reconnection": "Reconnection Settings", + "reconnection": "Nastavenia opätovného pripojenia", "max-attempts": "Max Reconnection Attempts", "default-placeholder": "Default: %1", "delay": "Reconnection Delay" diff --git a/public/language/sk/admin/settings/tags.json b/public/language/sk/admin/settings/tags.json index 6f31f60ba0..cc8ef4ca4d 100644 --- a/public/language/sk/admin/settings/tags.json +++ b/public/language/sk/admin/settings/tags.json @@ -1,5 +1,5 @@ { - "tag": "Tag Settings", + "tag": "Nastavenie reputácie", "min-per-topic": "Minimum Tags per Topic", "max-per-topic": "Maximum Tags per Topic", "min-length": "Minimum Tag Length", diff --git a/public/language/sk/admin/settings/user.json b/public/language/sk/admin/settings/user.json index b8f51c9288..bff94e851e 100644 --- a/public/language/sk/admin/settings/user.json +++ b/public/language/sk/admin/settings/user.json @@ -1,5 +1,5 @@ { - "authentication": "Authentication", + "authentication": "Overenie pravosti", "allow-local-login": "Allow local login", "require-email-confirmation": "Require Email Confirmation", "email-confirm-interval": "User may not resend a confirmation email until", @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/sk/admin/settings/web-crawler.json b/public/language/sk/admin/settings/web-crawler.json index 2e0d31d12b..49716691ba 100644 --- a/public/language/sk/admin/settings/web-crawler.json +++ b/public/language/sk/admin/settings/web-crawler.json @@ -1,5 +1,5 @@ { - "crawlability-settings": "Crawlability Settings", + "crawlability-settings": "Nastavenia indexového prehľadávania", "robots-txt": "Custom Robots.txt Leave blank for default", "sitemap-feed-settings": "Sitemap & Feed Settings", "disable-rss-feeds": "Disable RSS Feeds", diff --git a/public/language/sk/email.json b/public/language/sk/email.json index 11e7372009..8b665092ea 100644 --- a/public/language/sk/email.json +++ b/public/language/sk/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "Toto oznámenie o príspevkoch ste prijali na základe Vašich nastavení účtu.", "test.text1": "Toto je skúšobný e-mail na overenie funkčnosti e-mailovej aplikácie Vášho NodeBB fóra.", "unsub.cta": "Kliknite sem pre zmenu týchto nastavení", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "Boli ste zablokovaný používateľom %1", + "banned.text1": "Používateľ %1 bol zablokovaný používateľom %2.", + "banned.text2": "Toto zablokovanie bude trvať do %1.", + "banned.text3": "To je dôvod, prečo ste boli zablokovaný:", "closing": "Ďakujeme!" } \ No newline at end of file diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 76f54ce089..f387d6a128 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Nesprávne údaje", + "invalid-json": "Neplatné JSON", "not-logged-in": "Zdá sa že nie ste prihlásený.", "account-locked": "Váš účet bol dočasne uzamknutý", "search-requires-login": "K vyhľadávaniu je vyžadovaný účet - prosím prihláste sa alebo zaregistrujte.", @@ -12,6 +13,7 @@ "invalid-title": "Nesprávny nadpis!", "invalid-user-data": "Neplatné užívateľské údaje", "invalid-password": "Nesprávne heslo", + "invalid-login-credentials": "Neplatné prihlasovacie údaje", "invalid-username-or-password": "Prosím upresnite užívateľské meno a heslo", "invalid-search-term": "Neplatný hľadaný výraz", "csrf-invalid": "Nie sme schopný Vás znova prihlásiť, pravdepodobne kvôli uplynutiu relácie. Prosíme, zopakujte to neskôr", @@ -30,7 +32,7 @@ "password-too-long": "Heslo je príliš dlhé", "user-banned": "Užívateľ je zablokovaný", "user-banned-reason": "Prepáčte, tento účet bol zablokovaný (Dôvod: %1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "Ospravedlňujeme sa, tento účet bol zablokovaný do %1 (Dôvod: %2)", "user-too-new": "Prepáčte, musíte počkať %1 sekúnd(y) predtým, ako vytvoríte svoj prvý príspevok", "blacklisted-ip": "Prepáčte, ale vaša IP adresa bola na tejto komunite zablokovaná. Ak sa cítite poškodený, prosím kontaktujte správcu.", "ban-expiry-missing": "Prosím uveďte dátum ukončenia tohto zablokovania", @@ -105,7 +107,7 @@ "chat-disabled": "Systém konverzácií je zablokovaný", "too-many-messages": "Odoslali ste príliš veľa správ, počkajte chvíľu prosím.", "invalid-chat-message": "Neplatná správa konverzácie", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "Správy v konverzácií nemôžu byť dlhšie ako %1 znakov.", "cant-edit-chat-message": "Nemáte oprávnenie k úprave tejto správy", "cant-remove-last-user": "Nemôžete odstrániť posledného užívateľa", "cant-delete-chat-message": "Nemáte oprávanie k odstráneniu tejto správy", diff --git a/public/language/sk/flags.json b/public/language/sk/flags.json index 21bdc1bf31..281bfd6e14 100644 --- a/public/language/sk/flags.json +++ b/public/language/sk/flags.json @@ -1,5 +1,5 @@ { - "state": "State", + "state": "Stav", "reporter": "Reporter", "reported-at": "Reported At", "description": "Description", diff --git a/public/language/sk/global.json b/public/language/sk/global.json index 3d3705597c..9e19412427 100644 --- a/public/language/sk/global.json +++ b/public/language/sk/global.json @@ -103,5 +103,7 @@ "cookies.message": "Táto webová stránka používa cookies k tomu, aby bolo zaistené, že dostanete najlepší pôžitok s návštevy na našich webových stránkach.", "cookies.accept": "Mám to!", "cookies.learn_more": "Zistit viac", - "edited": "Zmenené" + "edited": "Zmenené", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/sk/modules.json b/public/language/sk/modules.json index 880a4a0128..e225ee6ead 100644 --- a/public/language/sk/modules.json +++ b/public/language/sk/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3 mesiace", "chat.delete_message_confirm": "Ste si istý, že chcete odstrániť túto správu?", "chat.add-users-to-room": "Pridať užívateľa do miestnosti", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "Tento používateľ nastavil svoj stav na DnD (Nevyrušovať). Stále chcete s nimi hovoriť?", "composer.compose": "Zostaviť", "composer.show_preview": "Zobraziť náhľad", "composer.hide_preview": "Skryť náhľad", diff --git a/public/language/sk/notifications.json b/public/language/sk/notifications.json index 5bf882e4d1..956d44f83c 100644 --- a/public/language/sk/notifications.json +++ b/public/language/sk/notifications.json @@ -17,7 +17,7 @@ "follows": "Nasledovatelia", "upvote": "Zahlasované", "new-flags": "Nové označenia", - "my-flags": "Flags assigned to me", + "my-flags": "Priradené značky", "bans": "Zablokované", "new_message_from": "Nova spáva od %1", "upvoted_your_post_in": "%1 dal hlas Vášmu príspevku v %2.", @@ -40,7 +40,7 @@ "user_started_following_you_multiple": "%1 a %2 ďalší Vás začali sledovať.", "new_register": "%1 odoslal žiadosť o registráciu.", "new_register_multiple": "Nachádzajú sa %1 registrácie čakajúce na preskúmanie.", - "flag_assigned_to_you": "Flag %1 has been assigned to you", + "flag_assigned_to_you": "Značka %1 vám bola priradená ", "email-confirmed": "E-mail bol potvrdený", "email-confirmed-message": "Ďakujeme za potvrdenie Vášho e-mailu. Účet je plne aktivovaný.", "email-confirm-error-message": "Vyskytla sa chyba pri overení Vašej e-mailovej adresy. ", diff --git a/public/language/sk/topic.json b/public/language/sk/topic.json index e10b2f7afa..d8662c3bf3 100644 --- a/public/language/sk/topic.json +++ b/public/language/sk/topic.json @@ -14,6 +14,7 @@ "quote": "Citovať", "reply": "Odpovedať", "replies_to_this_post": "%1 Odpovedí", + "one_reply_to_this_post": "1 Odpoveď", "last_reply_time": "Posledná odpoveď", "reply-as-topic": "Odpovedať ako téma", "guest-login-reply": "Pre odpoveď sa najprv prihláste", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Odomknúť tému", "thread_tools.move": "Presunúť tému", "thread_tools.move_all": "Presunúť všetko", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Rozvetviť tému", "thread_tools.delete": "Odstrániť tému", "thread_tools.delete-posts": "Odstrániť príspevky", @@ -71,7 +73,6 @@ "post_restore_confirm": "Ste si istí, že chcete obnoviť tento príspevok?", "post_purge_confirm": "Ste si istý že chcete naozaj vyčistiť tento príspevok?", "load_categories": "Načítanie kategórií", - "disabled_categories_note": "Zablokované kategórie sú sivé", "confirm_move": "Presunúť", "confirm_fork": "Rozdeliť", "bookmark": "Záložka", @@ -83,7 +84,6 @@ "move_post": "Presunúť príspevok", "post_moved": "Príspevok presunutý!", "fork_topic": "Rozdeliť príspevok", - "topic_will_be_moved_to": "Táto téma bude presunutá do kategórie", "fork_topic_instruction": "Vyber príspevky, ktoré chceš oddeliť", "fork_no_pids": "Žiadne príspevky neboli vybraté!", "fork_pid_count": "%1 príspevky(ov) vybraté", diff --git a/public/language/sl/admin/appearance/customise.json b/public/language/sl/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/sl/admin/appearance/customise.json +++ b/public/language/sl/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/sl/admin/development/info.json b/public/language/sl/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/sl/admin/development/info.json +++ b/public/language/sl/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/sl/admin/general/languages.json b/public/language/sl/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/sl/admin/general/languages.json +++ b/public/language/sl/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/sl/admin/manage/categories.json b/public/language/sl/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/sl/admin/manage/categories.json +++ b/public/language/sl/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/sl/admin/settings/user.json b/public/language/sl/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/sl/admin/settings/user.json +++ b/public/language/sl/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/sl/error.json b/public/language/sl/error.json index 6c876e99d4..78f719e7b3 100644 --- a/public/language/sl/error.json +++ b/public/language/sl/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Napačni podatki", + "invalid-json": "Invalid JSON", "not-logged-in": "Niste prijavljeni.", "account-locked": "Vaš račun je bil začasno zaklenjen.", "search-requires-login": "Iskanje zahteva uporabniški račun - prosimo, da se prijavite ali registrirate.", @@ -12,6 +13,7 @@ "invalid-title": "Napačen naslov", "invalid-user-data": "Napačni podatki o uporabniku", "invalid-password": "Napačno geslo", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Prosimo, vpišite uporabniško ime in geslo.", "invalid-search-term": "Napačen iskalni izraz", "csrf-invalid": "Prijava ni mogoča, verjetno zaradi potekle seje. Poskusite znova.", diff --git a/public/language/sl/global.json b/public/language/sl/global.json index 63c5fc210f..193a4780a7 100644 --- a/public/language/sl/global.json +++ b/public/language/sl/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/sl/topic.json b/public/language/sl/topic.json index 11fc2e87c6..cc539f3f40 100644 --- a/public/language/sl/topic.json +++ b/public/language/sl/topic.json @@ -14,6 +14,7 @@ "quote": "Citiraj", "reply": "Odgovori", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Odgovori s temo", "guest-login-reply": "Prijavi se za odgovor", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Odkleni temo", "thread_tools.move": "Premakni temo", "thread_tools.move_all": "Premakni vse", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Razcepi temo", "thread_tools.delete": "Izbriši temo", "thread_tools.delete-posts": "Izbriši objave", @@ -71,7 +73,6 @@ "post_restore_confirm": "Ste prepričani, da želite razveljaviti to objavo?", "post_purge_confirm": "Ste prepričani, da želite očistiti to objavo?", "load_categories": "Nalagam kategorije", - "disabled_categories_note": "Onemogočene kategorije so obarvane sivo", "confirm_move": "Premakni", "confirm_fork": "Razcepi", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Premakni objavo", "post_moved": "Objava premaknjena!", "fork_topic": "Razcepi temo", - "topic_will_be_moved_to": "Ta tema bo premaknjena v to kategorijo", "fork_topic_instruction": "Klikni na objavo, ki o želiš odcepiti", "fork_no_pids": "Ni izbranih objav!", "fork_pid_count": "Izbranih objav: %1 ", diff --git a/public/language/sr/admin/admin.json b/public/language/sr/admin/admin.json index 9c01f56006..c571378ac3 100644 --- a/public/language/sr/admin/admin.json +++ b/public/language/sr/admin/admin.json @@ -1,7 +1,7 @@ { - "alert.confirm-reload": "Are you sure you wish to reload NodeBB?", - "alert.confirm-restart": "Are you sure you wish to restart NodeBB?", + "alert.confirm-reload": "Da li želite da ponovo učitate NodeBB?", + "alert.confirm-restart": "Da li želite da restartujete NodeBB?", - "acp-title": "%1 | NodeBB Admin Control Panel", - "settings-header-contents": "Contents" + "acp-title": "%1 | NodeBB Administratorski panel", + "settings-header-contents": "Sadržaj" } \ No newline at end of file diff --git a/public/language/sr/admin/advanced/database.json b/public/language/sr/admin/advanced/database.json index b88ca6fc82..dfcd8313bd 100644 --- a/public/language/sr/admin/advanced/database.json +++ b/public/language/sr/admin/advanced/database.json @@ -1,36 +1,36 @@ { "x-b": "%1 b", - "x-mb": "%1 mb", - "x-gb": "%1 gb", + "x-mb": "%1 megabajt", + "x-gb": "%1 gigabajt", "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", "mongo": "Mongo", - "mongo.version": "MongoDB Version", + "mongo.version": "MongoDB verzija", "mongo.storage-engine": "Storage Engine", - "mongo.collections": "Collections", - "mongo.objects": "Objects", - "mongo.avg-object-size": "Avg. Object Size", - "mongo.data-size": "Data Size", + "mongo.collections": "Kolekcije", + "mongo.objects": "Objekti", + "mongo.avg-object-size": "Prosečna veličina objekta", + "mongo.data-size": "Veličina podatka", "mongo.storage-size": "Storage Size", - "mongo.index-size": "Index Size", - "mongo.file-size": "File Size", + "mongo.index-size": "Veličina Index-a", + "mongo.file-size": "Veličina Fajla", "mongo.resident-memory": "Resident Memory", - "mongo.virtual-memory": "Virtual Memory", - "mongo.mapped-memory": "Mapped Memory", - "mongo.raw-info": "MongoDB Raw Info", + "mongo.virtual-memory": "Virtuelna memorija", + "mongo.mapped-memory": "Mapirana Memorija", + "mongo.raw-info": "Sirove informacije o MongoDB", "redis": "Redis", - "redis.version": "Redis Version", - "redis.connected-clients": "Connected Clients", - "redis.connected-slaves": "Connected Slaves", - "redis.blocked-clients": "Blocked Clients", - "redis.used-memory": "Used Memory", - "redis.memory-frag-ratio": "Memory Fragmentation Ratio", - "redis.total-connections-recieved": "Total Connections Received", - "redis.total-commands-processed": "Total Commands Processed", - "redis.iops": "Instantaneous Ops. Per Second", + "redis.version": "Redis verzija", + "redis.connected-clients": "Klijenata povezano", + "redis.connected-slaves": "Povezano \"robova\"", + "redis.blocked-clients": "Klijenata blokirano", + "redis.used-memory": "Iskorišćena memorija", + "redis.memory-frag-ratio": "Odnos fragmentisane memorije", + "redis.total-connections-recieved": "Ukupno primljeno konekcija", + "redis.total-commands-processed": "Ukupno komandi procesuirano", + "redis.iops": "Trenutno operacija po sekundi", "redis.keyspace-hits": "Keyspace Hits", "redis.keyspace-misses": "Keyspace Misses", - "redis.raw-info": "Redis Raw Info" + "redis.raw-info": "Sirove informacije o Redis-u" } \ No newline at end of file diff --git a/public/language/sr/admin/appearance/customise.json b/public/language/sr/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/sr/admin/appearance/customise.json +++ b/public/language/sr/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/sr/admin/development/info.json b/public/language/sr/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/sr/admin/development/info.json +++ b/public/language/sr/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/sr/admin/general/languages.json b/public/language/sr/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/sr/admin/general/languages.json +++ b/public/language/sr/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/sr/admin/manage/categories.json b/public/language/sr/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/sr/admin/manage/categories.json +++ b/public/language/sr/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/sr/admin/menu.json b/public/language/sr/admin/menu.json index 985c540e8a..f56feb43d7 100644 --- a/public/language/sr/admin/menu.json +++ b/public/language/sr/admin/menu.json @@ -1,74 +1,74 @@ { - "section-general": "General", - "general/dashboard": "Dashboard", - "general/homepage": "Home Page", - "general/navigation": "Navigation", - "general/languages": "Languages", - "general/sounds": "Sounds", - "general/social": "Social", + "section-general": "Uopšteno", + "general/dashboard": "Komandna tabla", + "general/homepage": "Home stranica", + "general/navigation": "Navigacija", + "general/languages": "Jezici", + "general/sounds": "Zvukovi", + "general/social": "Društveno", - "section-manage": "Manage", - "manage/categories": "Categories", - "manage/tags": "Tags", - "manage/users": "Users", - "manage/registration": "Registration Queue", - "manage/groups": "Groups", - "manage/ip-blacklist": "IP Blacklist", + "section-manage": "Menadžment", + "manage/categories": "Kategorije", + "manage/tags": "Tagovi", + "manage/users": "Korisnici", + "manage/registration": "Lista Registracija", + "manage/groups": "Grupe", + "manage/ip-blacklist": "Crna Lista IP adresa", - "section-settings": "Settings", - "settings/general": "General", - "settings/reputation": "Reputation", + "section-settings": "Podešavanja", + "settings/general": "Uopšteno", + "settings/reputation": "Reputacija", "settings/email": "Email", - "settings/user": "User", - "settings/group": "Group", - "settings/guest": "Guests", - "settings/uploads": "Uploads", - "settings/post": "Post", - "settings/chat": "Chat", + "settings/user": "Korisnik", + "settings/group": "Grupa", + "settings/guest": "Gosti", + "settings/uploads": "Otpremljene datoteke", + "settings/post": "Poruka", + "settings/chat": "Ćaskanje", "settings/pagination": "Pagination", "settings/tags": "Tags", - "settings/notifications": "Notifications", + "settings/notifications": "Notifikacije", "settings/cookies": "Cookies", "settings/web-crawler": "Web Crawler", "settings/sockets": "Sockets", - "settings/advanced": "Advanced", + "settings/advanced": "Napredno", - "settings.page-title": "%1 Settings", + "settings.page-title": "%1 Podešavanja", - "section-appearance": "Appearance", - "appearance/themes": "Themes", + "section-appearance": "Izgled", + "appearance/themes": "Teme", "appearance/skins": "Skins", "appearance/customise": "Custom HTML & CSS", - "section-extend": "Extend", - "extend/plugins": "Plugins", - "extend/widgets": "Widgets", - "extend/rewards": "Rewards", + "section-extend": "Proširiti", + "extend/plugins": "Plaginovi", + "extend/widgets": "Vidžeti", + "extend/rewards": "Nagrade", - "section-social-auth": "Social Authentication", + "section-social-auth": "Auntentifikacija sa društvenih mreža", "section-plugins": "Plugins", - "extend/plugins.install": "Install Plugins", + "extend/plugins.install": "Instaliraj plaginove", - "section-advanced": "Advanced", - "advanced/database": "Database", - "advanced/events": "Events", - "advanced/logs": "Logs", - "advanced/errors": "Errors", + "section-advanced": "Napredno", + "advanced/database": "Baza podataka", + "advanced/events": "Događaji", + "advanced/logs": "Izveštaji", + "advanced/errors": "Greške", "advanced/cache": "Cache", - "development/logger": "Logger", + "development/logger": "Loger", "development/info": "Info", - "reload-forum": "Reload Forum", - "restart-forum": "Restart Forum", - "logout": "Log out", - "view-forum": "View Forum", + "reload-forum": "Ponovo učitaj podešavanja Foruma", + "restart-forum": "Ponovo učitaj forum", + "logout": "Izloguj se", + "view-forum": "Pogledaj Forum", - "search.placeholder": "Search...", - "search.no-results": "No results...", - "search.search-forum": "Search the forum for ", - "search.keep-typing": "Type more to see results...", - "search.start-typing": "Start typing to see results...", + "search.placeholder": "Pretraga...", + "search.no-results": "Nema rezultata...", + "search.search-forum": "Pretraži forum za ", + "search.keep-typing": "Ukucaj više da vidiš rezultate", + "search.start-typing": "Počni da kucaš da vidiš rezultate...", - "connection-lost": "Connection to %1 has been lost, attempting to reconnect..." + "connection-lost": "Konekcija ka %1 je izgubljena, pokušavam ponovo da se konektujem..." } \ No newline at end of file diff --git a/public/language/sr/admin/settings/user.json b/public/language/sr/admin/settings/user.json index b8f51c9288..a9e9709e24 100644 --- a/public/language/sr/admin/settings/user.json +++ b/public/language/sr/admin/settings/user.json @@ -1,60 +1,62 @@ { - "authentication": "Authentication", - "allow-local-login": "Allow local login", - "require-email-confirmation": "Require Email Confirmation", - "email-confirm-interval": "User may not resend a confirmation email until", - "email-confirm-email2": "minutes have elapsed", - "allow-login-with": "Allow login with", - "allow-login-with.username-email": "Username or Email", - "allow-login-with.username": "Username Only", - "allow-login-with.email": "Email Only", - "account-settings": "Account Settings", - "disable-username-changes": "Disable username changes", - "disable-email-changes": "Disable email changes", - "disable-password-changes": "Disable password changes", - "allow-account-deletion": "Allow account deletion", - "user-info-private": "Make user info private", - "themes": "Themes", - "disable-user-skins": "Prevent users from choosing a custom skin", - "account-protection": "Account Protection", - "login-attempts": "Login attempts per hour", - "login-attempts-help": "If login attempts to a user's account exceeds this threshold, that account will be locked for a pre-configured amount of time", - "lockout-duration": "Account Lockout Duration (minutes)", + "authentication": "Auntentifikacija", + "allow-local-login": "Dozvoli lokalni login", + "require-email-confirmation": "Zahteva Email konfirmaciju", + "email-confirm-interval": "Korisnik možda neće moći da ponovo pošalje email konfirmaciju sve dok", + "email-confirm-email2": "minuta je prošlo", + "allow-login-with": "Dozvoli login sa", + "allow-login-with.username-email": "Korisničko ime ili Email", + "allow-login-with.username": "Samo korisničko ime", + "allow-login-with.email": "Samo Email", + "account-settings": "Podešavanje naloga", + "disable-username-changes": "Onemogući promenu korisničkog imena", + "disable-email-changes": "Onemogući promenu email-a", + "disable-password-changes": "Onemogući promenu šifre", + "allow-account-deletion": "Dozvoli brisanje naloga", + "user-info-private": "Načini korisničke podatke privatnim", + "themes": "Teme", + "disable-user-skins": "Onemogući korisnike da izaberu određenu temu", + "account-protection": "Začtita naloga", + "login-attempts": "Dozvoljeno logovanje po satu", + "login-attempts-help": "Ako broj logovanja prema user's predje određenu granicu, taj nalog može biti zaključan na određeno prekonfigurisano vreme", + "lockout-duration": "Trajanje dok se nalog ne otključa (minuta)", "login-days": "Days to remember user login sessions", - "password-expiry-days": "Force password reset after a set number of days", - "registration": "User Registration", - "registration-type": "Registration Type", - "registration-type.normal": "Normal", - "registration-type.admin-approval": "Admin Approval", - "registration-type.admin-approval-ip": "Admin Approval for IPs", - "registration-type.invite-only": "Invite Only", - "registration-type.admin-invite-only": "Admin Invite Only", - "registration-type.disabled": "No registration", - "registration-type.help": "Normal - Users can register from the /register page.
\nAdmin Approval - User registrations are placed in an approval queue for administrators.
\nAdmin Approval for IPs - Normal for new users, Admin Approval for IP addresses that already have an account.
\nInvite Only - Users can invite others from the users page.
\nAdmin Invite Only - Only administrators can invite others from users and admin/manage/users pages.
\nNo registration - No user registration.
", - "registration.max-invites": "Maximum Invitations per User", + "password-expiry-days": "Forsiraj resetovanje lozinke nakon odredjenog broja dana", + "registration": "Registracija korisnika", + "registration-type": "Tip registracije", + "registration-type.normal": "Normalno", + "registration-type.admin-approval": "Administratorsko odobravanje", + "registration-type.admin-approval-ip": "Administratosko odobravanje za IP", + "registration-type.invite-only": "Samo pozivnica", + "registration-type.admin-invite-only": "Samo administratorsko pozivanje", + "registration-type.disabled": "Nema registracije", + "registration-type.help": "Regularno - Korisnici mogu da se registruju na /register stranici.
\nAdministratorsko odobravanje - Registracije korisnika su plasirane u red za odobravanje za administratore.
\n\nAdministratorsko odobravanje za IP - Regularno za nove korisnike, administratorsko odobravanje za IP adrese koje već imaju nalog.
\nSamo pozivnica - Korisnici mogu da pozovu druge sa korisničke stranice.
\nSamo administratorsko pozivanje - samo administratori mogu pozvati druge sa korisničke i admin/manage/users stranice.
\nBez registracije - nema registracije novih korisnika.
", + "registration.max-invites": "Maksimum poziva po korisniku.", "max-invites": "Maximum Invitations per User", - "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", - "min-username-length": "Minimum Username Length", - "max-username-length": "Maximum Username Length", - "min-password-length": "Minimum Password Length", - "min-password-strength": "Minimum Password Strength", - "max-about-me-length": "Maximum About Me Length", - "terms-of-use": "Forum Terms of Use (Leave blank to disable)", - "user-search": "User Search", - "user-search-results-per-page": "Number of results to display", - "default-user-settings": "Default User Settings", - "show-email": "Show email", - "show-fullname": "Show fullname", - "restrict-chat": "Only allow chat messages from users I follow", - "outgoing-new-tab": "Open outgoing links in new tab", - "topic-search": "Enable In-Topic Searching", - "digest-freq": "Subscribe to Digest", - "digest-freq.off": "Off", - "digest-freq.daily": "Daily", - "digest-freq.weekly": "Weekly", - "digest-freq.monthly": "Monthly", - "email-chat-notifs": "Send an email if a new chat message arrives and I am not online", - "email-post-notif": "Send an email when replies are made to topics I am subscribed to", - "follow-created-topics": "Follow topics you create", - "follow-replied-topics": "Follow topics that you reply to" + "max-invites-help": "0 za bez restrikcija. Administratori dobijaju bezgranično pozivnica
Samo određeni za \"Samo pozivnica\"", + "invite-expiration": "Isticanje pozivnice", + "invite-expiration-help": "# dana kada ističe pozivnica.", + "min-username-length": "Minimum karaktera u korisničkom imenu", + "max-username-length": "Maksimum karaktera u korisničkom imenu", + "min-password-length": "Minimum karaktera u lozinci", + "min-password-strength": "Minimalna jačina lozinke", + "max-about-me-length": "Maksimum karaktera O Meni", + "terms-of-use": "Uslovi upotrebe foruma (Ostavite prazno da onemogućite)", + "user-search": "Pretraga Korisnika", + "user-search-results-per-page": "Broj rezultata po prikazu", + "default-user-settings": "Uobičajne Postavke Korisnika", + "show-email": "Prikaži email", + "show-fullname": "Prikaži puno ime", + "restrict-chat": "Samo dozvoli chat poruke korisnika koje ja pratim", + "outgoing-new-tab": "Otvori odlazeće linove u novom tabu", + "topic-search": "Omogući pretraživanje u temi", + "digest-freq": "Pretplatite se na Digest", + "digest-freq.off": "Isključeno", + "digest-freq.daily": "Dnevno", + "digest-freq.weekly": "Nedeljno", + "digest-freq.monthly": "Mesečno", + "email-chat-notifs": "Pošalji email ako nova chat poruka stigne dok nisam online", + "email-post-notif": "Pošalji email kada odgovori su načinjeni u temu u kojoj sam ja pretplaćen", + "follow-created-topics": "Prati teme koje si ti napravio", + "follow-replied-topics": "Prati teme na koje si ti odgovorio" } \ No newline at end of file diff --git a/public/language/sr/admin/settings/web-crawler.json b/public/language/sr/admin/settings/web-crawler.json index 2e0d31d12b..3b03ea1a92 100644 --- a/public/language/sr/admin/settings/web-crawler.json +++ b/public/language/sr/admin/settings/web-crawler.json @@ -1,10 +1,10 @@ { - "crawlability-settings": "Crawlability Settings", - "robots-txt": "Custom Robots.txt Leave blank for default", - "sitemap-feed-settings": "Sitemap & Feed Settings", - "disable-rss-feeds": "Disable RSS Feeds", - "disable-sitemap-xml": "Disable Sitemap.xml", - "sitemap-topics": "Number of Topics to display in the Sitemap", - "clear-sitemap-cache": "Clear Sitemap Cache", - "view-sitemap": "View Sitemap" + "crawlability-settings": "Podešsavanje crawl-ovanja", + "robots-txt": "Napredni Robots.txt Ostavite prazno za uobičajena podešavanja", + "sitemap-feed-settings": "Mapa sajta i podešavanje Feed-a", + "disable-rss-feeds": "Onemogući RSS Feed", + "disable-sitemap-xml": "Onemogući Sitemap.xml", + "sitemap-topics": "Broj Tema za prikaz u Mapi sajta", + "clear-sitemap-cache": "Obroši cache Mape sajta", + "view-sitemap": "Pogledaj Mapu sajta" } \ No newline at end of file diff --git a/public/language/sr/error.json b/public/language/sr/error.json index f05560c926..61708b7f2b 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Неисправни подаци", + "invalid-json": "Неважећи JSON", "not-logged-in": "Изгледа да нисте пријављени.", "account-locked": "Ваш налог је привремено закључан", "search-requires-login": "Претраживање захтева налог — пријавите се или се региструјте.", @@ -12,6 +13,7 @@ "invalid-title": "Неисправан наслов!", "invalid-user-data": "Неисправни кориснички подаци", "invalid-password": "Неисправна лозинка", + "invalid-login-credentials": "Неважећи акредитиви за пријављивање", "invalid-username-or-password": "Молимо наведите и корисничко име и лозинку", "invalid-search-term": "Неисправан упит за претрагу", "csrf-invalid": "Нисмо успели да вас пријавимо, вероватно због истека сесије. Молимо покушајте поново", diff --git a/public/language/sr/global.json b/public/language/sr/global.json index a66b424eef..62e689cf71 100644 --- a/public/language/sr/global.json +++ b/public/language/sr/global.json @@ -103,5 +103,7 @@ "cookies.message": "Овај веб сајт користи колачиће да би вам обезбедили најбољи доживљај на нашем сајту.", "cookies.accept": "Схватам!", "cookies.learn_more": "Сазнајте више", - "edited": "Уређено" + "edited": "Уређено", + "disabled": "Онемогућено", + "select": "Изабери" } \ No newline at end of file diff --git a/public/language/sr/topic.json b/public/language/sr/topic.json index 95655a8162..e104832976 100644 --- a/public/language/sr/topic.json +++ b/public/language/sr/topic.json @@ -14,6 +14,7 @@ "quote": "Цитирај", "reply": "Одговори", "replies_to_this_post": "Одговора: %1", + "one_reply_to_this_post": "1 одговор", "last_reply_time": "Последњи одговор", "reply-as-topic": "Постави одговор као тему", "guest-login-reply": "Пријавите се да бисте одговорили", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Откључај тему", "thread_tools.move": "Премести тему", "thread_tools.move_all": "Премести све", + "thread_tools.select_category": "Изаберите категорију", "thread_tools.fork": "Рачвај тему", "thread_tools.delete": "Избриши тему", "thread_tools.delete-posts": "Избриши поруку", @@ -71,7 +73,6 @@ "post_restore_confirm": "Да ли сте сигурни да желите да обновите ову поруку?", "post_purge_confirm": "Да ли сте сигурни да желите да очистите овај пост?", "load_categories": "Учитавање категорија", - "disabled_categories_note": "Онемогућене категорије су недоступне", "confirm_move": "Премести", "confirm_fork": "Рачвај", "bookmark": "Обележивач", @@ -83,7 +84,6 @@ "move_post": "Премести поруку", "post_moved": "Порука је премештена!", "fork_topic": "Рачвај тему", - "topic_will_be_moved_to": "Ова тема ће бити премештена у категорију", "fork_topic_instruction": "Кликните на поруке које желите да рачвате", "fork_no_pids": "Нема одабраних порука!", "fork_pid_count": "Одабрано порука: %1", diff --git a/public/language/sr/user.json b/public/language/sr/user.json index 3949589e7b..7b7442001e 100644 --- a/public/language/sr/user.json +++ b/public/language/sr/user.json @@ -67,7 +67,7 @@ "upload_cover_picture": "Отпреми насловну слику", "remove_cover_picture_confirm": "Да ли сте сигурни да желите да уклоните насловну слику?", "crop_picture": "Изрежи слику", - "upload_cropped_picture": "Изрежи и опреми", + "upload_cropped_picture": "Изрежи и отпреми", "settings": "Подешавања", "show_email": "Прикажи моју лозинку", "show_fullname": "Прикажи моје пуно име", diff --git a/public/language/sv/admin/appearance/customise.json b/public/language/sv/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/sv/admin/appearance/customise.json +++ b/public/language/sv/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/sv/admin/development/info.json b/public/language/sv/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/sv/admin/development/info.json +++ b/public/language/sv/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/sv/admin/general/languages.json b/public/language/sv/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/sv/admin/general/languages.json +++ b/public/language/sv/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/sv/admin/manage/categories.json b/public/language/sv/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/sv/admin/manage/categories.json +++ b/public/language/sv/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/sv/admin/settings/user.json b/public/language/sv/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/sv/admin/settings/user.json +++ b/public/language/sv/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 2e3ea4bfcb..63f0110096 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Ogiltig data", + "invalid-json": "Invalid JSON", "not-logged-in": "Du verkar inte vara inloggad.", "account-locked": "Ditt konto har tillfälligt blivit låst", "search-requires-login": "Sökning kräver ett konto, var god logga in eller registrera dig.", @@ -12,6 +13,7 @@ "invalid-title": "Ogiltig rubrik!", "invalid-user-data": "Ogiltig användardata", "invalid-password": "Ogiltigt lösenord", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Specificera både användarnamn och lösenord", "invalid-search-term": "Ogiltig sökterm", "csrf-invalid": "Det gick inte att logga in dig, sannolikt på grund av en utgången session. Var god försök igen", diff --git a/public/language/sv/global.json b/public/language/sv/global.json index 6fcfe965df..f24cdffb00 100644 --- a/public/language/sv/global.json +++ b/public/language/sv/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json index 45599c2186..bf51d85095 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -14,6 +14,7 @@ "quote": "Citera", "reply": "Svara", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "Svara som ämne", "guest-login-reply": "Logga in för att posta", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Lås upp ämne", "thread_tools.move": "Flytta ämne", "thread_tools.move_all": "Flytta alla", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Grena ämne", "thread_tools.delete": "Ta bort ämne", "thread_tools.delete-posts": "Radera inlägg", @@ -71,7 +73,6 @@ "post_restore_confirm": "Är du säker på att du vill återställa det här inlägget?", "post_purge_confirm": "Är du säker att du vill rensa bort det här inlägget?", "load_categories": "Laddar kategorier", - "disabled_categories_note": "Inaktiverade kategorier är utgråade", "confirm_move": "Flytta", "confirm_fork": "Grena", "bookmark": "Bokmärke", @@ -83,7 +84,6 @@ "move_post": "Flytta inlägg", "post_moved": "Inlägget flyttades.", "fork_topic": "Grena ämne", - "topic_will_be_moved_to": "Detta ämne kommer att flyttas till kategorin", "fork_topic_instruction": "Klicka på de inlägg du vill grena", "fork_no_pids": "Inga inlägg valda!", "fork_pid_count": "%1 inlägg vald(a)", diff --git a/public/language/th/admin/appearance/customise.json b/public/language/th/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/th/admin/appearance/customise.json +++ b/public/language/th/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/th/admin/development/info.json b/public/language/th/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/th/admin/development/info.json +++ b/public/language/th/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/th/admin/general/languages.json b/public/language/th/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/th/admin/general/languages.json +++ b/public/language/th/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/th/admin/manage/categories.json b/public/language/th/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/th/admin/manage/categories.json +++ b/public/language/th/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/th/admin/settings/user.json b/public/language/th/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/th/admin/settings/user.json +++ b/public/language/th/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/th/error.json b/public/language/th/error.json index 106c4ad90a..3e3fac5363 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -1,5 +1,6 @@ { "invalid-data": "ข้อมูลไม่ถูกต้อง", + "invalid-json": "Invalid JSON", "not-logged-in": "คุณยังไม่ได้ลงชื่อเข้าระบบ", "account-locked": "บัญชีของคุณถูกระงับการใช้งานชั่วคราว", "search-requires-login": "\"ฟังก์ชั่นการค้นหา\" ต้องการบัญชีผู้ใช้ กรุณาเข้าสู่ระบบหรือสมัครสมาชิก", @@ -12,6 +13,7 @@ "invalid-title": "คำนำหน้าชื่อไม่ถูกต้อง", "invalid-user-data": "User Data ไม่ถูกต้อง", "invalid-password": "รหัสผ่านไม่ถูกต้อง", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "กรุณาระบุชื่อผู้ใช้และรหัสผ่าน", "invalid-search-term": "ข้อความค้นหาไม่ถูกต้อง", "csrf-invalid": "เราไม่สามารถนำท่านเข้าสู่ระบบได้ เหมือนกับว่าเซสชั่นหมดอายุแล้ว กรุณาลองใหม่อีกครั้ง", diff --git a/public/language/th/global.json b/public/language/th/global.json index 17b15b538c..ac6b834e34 100644 --- a/public/language/th/global.json +++ b/public/language/th/global.json @@ -103,5 +103,7 @@ "cookies.message": "เว็บไวต์นี้ใช้คุกกี้เพื่อที่จะทำให้แน่ใจว่า คุณได้รับประสบการณ์ที่เยี่ยมยอดที่สุดในการเข้าใช้เว็บไซต์ของเรา", "cookies.accept": "เข้าใจแล้ว!!! ", "cookies.learn_more": "เรียนรู้เพิ่มเติม", - "edited": "ถูกแก้ไขแล้ว" + "edited": "ถูกแก้ไขแล้ว", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/th/topic.json b/public/language/th/topic.json index edb408104c..82108b7a0f 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -14,6 +14,7 @@ "quote": "คำอ้างอิง", "reply": "ตอบ", "replies_to_this_post": " %1 คำตอบ", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "คำตอบล่าสุด", "reply-as-topic": "ตอบโดยตั้งกระทู้ใหม่", "guest-login-reply": "เข้าสู่ระบบเพื่อตอบกลับ", @@ -58,6 +59,7 @@ "thread_tools.unlock": "ปลดล็อคกระทู้", "thread_tools.move": "ย้ายกระทู้", "thread_tools.move_all": "ย้ายทั้งหมด", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "แยกกระทู้", "thread_tools.delete": "ลบกระทู้", "thread_tools.delete-posts": "ลบโพสต์", @@ -71,7 +73,6 @@ "post_restore_confirm": "คุณแน่ใจแล้วใช้ไหมว่าต้องการกู้คืนโพสต์นี้", "post_purge_confirm": "คุณแน่ใจแล้วใช่ไหมว่าต้องการล้างโพสต์นี้", "load_categories": "กำลังโหลดหมวดหมู่", - "disabled_categories_note": "หมวดหมู่ที่ปิดใช้งานจะเป็นสีเทา", "confirm_move": "ย้าย", "confirm_fork": "แยก", "bookmark": "บุ๊กมาร์ก", @@ -83,7 +84,6 @@ "move_post": "ย้ายโพส", "post_moved": "โพสต์ถูกย้ายแล้ว!", "fork_topic": "แยกกระทู้", - "topic_will_be_moved_to": "กระทู้นี้จะถูกย้ายไปที่หมวดหมู่", "fork_topic_instruction": "คลิกที่โพสที่คุณต้องการที่จะแยก", "fork_no_pids": "ไม่มีโพสต์ที่เลือก!", "fork_pid_count": " %1 โพสต์(s) ที่เลือก", diff --git a/public/language/th/user.json b/public/language/th/user.json index 6eafd81abe..850e87fba3 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -11,7 +11,7 @@ "ban_account_confirm": "คุณต้องการแบนผู้ใช้นี้หรือไม่?", "unban_account": "ปลดแบน", "delete_account": "ลบบัญชี", - "delete_account_confirm": "คุณต้องการลบบัญชีของคุณใช่หรือไม่?
การกระทำนี้ไม่สามารถกู้คืนได้ ข้อมูลบัญชีของคุณจะถูกลบทั้งหมด

กรอกชื่อผู้ใช้ของคุณ เพื่อยืนยันการลบบัญชีของ", + "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

Enter your username to confirm that you wish to destroy this account.", "delete_this_account_confirm": "คุณต้องการลบบัญชีนี้ใช่หรือไม่?
การกระทำนี้ไม่สามารถกู้คืนได้ ข้อมูลบัญชีนี้จะถูกลบทั้งหมด", "account-deleted": "บัญชีถูกลบแล้ว", "fullname": "ชื่อเต็ม", @@ -37,7 +37,7 @@ "follow": "ติดตาม", "unfollow": "เลิกติดตาม", "more": "เพิ่มเติม", - "profile_update_success": "ข้อมูลประวัติส่วนตัวได้รับการแก้ไขแล้ว", + "profile_update_success": "Profile has been updated successfully!", "change_picture": "เปลี่ยนรูป", "change_username": "เปลี่ยนชื่อผู้ใช้", "change_email": "เปลี่ยนอีเมล", @@ -73,8 +73,8 @@ "show_fullname": "แสดงชื่อจริงของฉัน", "restrict_chats": "รับข้อความสนทนาจากคนที่ฉันติดตามเท่านั้น", "digest_label": "สมัครรับข่าวสารจาก Digest", - "digest_description": "สมัครรับอีเมลอัพเดทข้อมูลของบอร์ดสนทนา (ข้อความแจ้งเตือนและหัวข้อใหม่ๆ) ตามรายการที่ตั้งไว้", - "digest_off": "ปิด", + "digest_description": "Subscribe to email updates for this forum (new notifications and topics) according to a set schedule", + "digest_off": "Off", "digest_daily": "รายวัน", "digest_weekly": "รายสัปดาห์", "digest_monthly": "รายเดือน", diff --git a/public/language/tr/admin/appearance/customise.json b/public/language/tr/admin/appearance/customise.json index afcc788881..dbb54c7b1f 100644 --- a/public/language/tr/admin/appearance/customise.json +++ b/public/language/tr/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Özel Header", "custom-header.description": "Forumunuzun biçimlendirmesini sağlayacak <head> bölümüne eklenecek özel HTML'yi (ör. JavaScript, Meta Etiketler vb.) Girin.", - "custom-header.enable": "Özel Header'ı Etkinleştir" + "custom-header.enable": "Özel Header'ı Etkinleştir", + + "custom-css.livereload": "Canlı Yenilemeyi Etkinleştir", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/tr/admin/development/info.json b/public/language/tr/admin/development/info.json index 594ac11906..cd8aa5fdbf 100644 --- a/public/language/tr/admin/development/info.json +++ b/public/language/tr/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/tr/admin/general/languages.json b/public/language/tr/admin/general/languages.json index 7767668448..9ca87d7776 100644 --- a/public/language/tr/admin/general/languages.json +++ b/public/language/tr/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Dil Ayarları", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Varsayılan Dil", + "auto-detect": "Ziyaretçiler için dili otomatik tespit et" } \ No newline at end of file diff --git a/public/language/tr/admin/manage/categories.json b/public/language/tr/admin/manage/categories.json index 930d9590bf..eb68238a46 100644 --- a/public/language/tr/admin/manage/categories.json +++ b/public/language/tr/admin/manage/categories.json @@ -10,15 +10,16 @@ "custom-class": "Özel Sınıf", "num-recent-replies": "# of Recent Replies", "ext-link": "Harici Bağlantı", + "is-section": "Treat this category as a section", "upload-image": "Görsel Yükle", "delete-image": "Sil", "category-image": "Kategori Görseli", - "parent-category": "Parent Category", + "parent-category": "Ebeveyn Kategori", "optional-parent-category": "(Optional) Parent Category", "parent-category-none": "(Hiçbiri)", "copy-settings": "Copy Settings From", "optional-clone-settings": "(Optional) Clone Settings From Category", - "purge": "Purge Category", + "purge": "Kategoriyi Temizle", "enable": "Etkinleştir", "disable": "Devredışı", @@ -31,7 +32,7 @@ "privileges.warning": "Note: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.", "privileges.section-viewing": "Viewing Privileges", "privileges.section-posting": "Posting Privileges", - "privileges.section-moderation": "Moderation Privileges", + "privileges.section-moderation": "Moderatörlük Ayrıcalıkları", "privileges.section-user": "Kullanıcı", "privileges.search-user": "Kullanıcı Ekle", "privileges.no-users": "No user-specific privileges in this category.", @@ -50,19 +51,19 @@ "analytics.posts-daily": "Figure 4 – Daily posts made in this category", "alert.created": "Yaratıldı", - "alert.create-success": "Category successfully created!", + "alert.create-success": "Kategori başarıyla yaratıldı!", "alert.none-active": "You have no active categories.", "alert.create": "Bir Kategori Yarat", "alert.confirm-moderate": "Are you sure you wish to grant the moderation privilege to this user group? This group is public, and any users can join at will.", "alert.confirm-purge": "

Do you really want to purge this category \"%1\"?

Warning! All topics and posts in this category will be purged!

Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category temporarily, you'll want to \"disable\" the category instead.

", "alert.purge-success": "Category purged!", - "alert.copy-success": "Settings Copied!", + "alert.copy-success": "Ayarlar Kopyalandı!", "alert.set-parent-category": "Set Parent Category", "alert.updated": "Updated Categories", "alert.updated-success": "Category IDs %1 successfully updated.", "alert.upload-image": "Kategori görseli yükle", "alert.find-user": "Bir Kullanıcı Ara", "alert.user-search": "Search for a user here...", - "alert.find-group": "Find a Group", + "alert.find-group": "Bir Grup Ara", "alert.group-search": "Search for a group here..." } \ No newline at end of file diff --git a/public/language/tr/admin/settings/reputation.json b/public/language/tr/admin/settings/reputation.json index d2eeff7b75..76f75d840e 100644 --- a/public/language/tr/admin/settings/reputation.json +++ b/public/language/tr/admin/settings/reputation.json @@ -1,6 +1,6 @@ { "reputation": "Oylama Ayarları", - "disable": "Disable Reputation System", + "disable": "Oylama Sistemini Devredışı", "disable-down-voting": "Eksi oyu devredışı bırak", "votes-are-public": "All Votes Are Public", "thresholds": "Activity Thresholds", diff --git a/public/language/tr/admin/settings/user.json b/public/language/tr/admin/settings/user.json index 0aa97f45e8..d0037cf8fc 100644 --- a/public/language/tr/admin/settings/user.json +++ b/public/language/tr/admin/settings/user.json @@ -1,7 +1,7 @@ { "authentication": "Kimlik Doğrulama", - "allow-local-login": "Allow local login", - "require-email-confirmation": "Require Email Confirmation", + "allow-local-login": "Yerel girişe izin ver", + "require-email-confirmation": "E-posta Onayı Gerektirir", "email-confirm-interval": "User may not resend a confirmation email until", "email-confirm-email2": "minutes have elapsed", "allow-login-with": "Allow login with", @@ -16,7 +16,7 @@ "user-info-private": "Kullanıcı bilgilerini gizli yap", "themes": "Temalar", "disable-user-skins": "Prevent users from choosing a custom skin", - "account-protection": "Account Protection", + "account-protection": "Hesap Koruma", "login-attempts": "Login attempts per hour", "login-attempts-help": "If login attempts to a user's account exceeds this threshold, that account will be locked for a pre-configured amount of time", "lockout-duration": "Account Lockout Duration (minutes)", @@ -34,17 +34,19 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", - "min-username-length": "Minimum Username Length", - "max-username-length": "Maximum Username Length", - "min-password-length": "Minimum Password Length", - "min-password-strength": "Minimum Password Strength", - "max-about-me-length": "Maximum About Me Length", - "terms-of-use": "Forum Terms of Use (Leave blank to disable)", - "user-search": "User Search", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", + "min-username-length": "Minimum Kullanıcı Adı Uzunluğu", + "max-username-length": "Maksimum Kullanıcı Adı Uzunluğu", + "min-password-length": "Minimum Parola Uzunluğu", + "min-password-strength": "Minimum Parola Gücü", + "max-about-me-length": "Maksimum Hakkımda Uzunluğu", + "terms-of-use": "Forum Kullanım Şartları (Devre dışı bırakmak için boş bırakın) ", + "user-search": "Kullanıcı Ara", "user-search-results-per-page": "Number of results to display", "default-user-settings": "Default User Settings", - "show-email": "Show email", - "show-fullname": "Show fullname", + "show-email": "Email Göster", + "show-fullname": "Full Adı Göster", "restrict-chat": "Only allow chat messages from users I follow", "outgoing-new-tab": "Open outgoing links in new tab", "topic-search": "Enable In-Topic Searching", diff --git a/public/language/tr/admin/settings/web-crawler.json b/public/language/tr/admin/settings/web-crawler.json index 063628507e..06ad724d67 100644 --- a/public/language/tr/admin/settings/web-crawler.json +++ b/public/language/tr/admin/settings/web-crawler.json @@ -1,10 +1,10 @@ { "crawlability-settings": "Taranabilirlik Ayarları", - "robots-txt": "Custom Robots.txt Leave blank for default", - "sitemap-feed-settings": "Sitemap & Feed Settings", - "disable-rss-feeds": "Disable RSS Feeds", + "robots-txt": "Özel Robots.txtVarsayılan olarak bırakmak için boş bırak", + "sitemap-feed-settings": "Site Haritası & Besleyici Ayarları", + "disable-rss-feeds": "RSS Besleyicilerini Devre dışı bırak", "disable-sitemap-xml": "sitemap.xml devredışı bırak", - "sitemap-topics": "Number of Topics to display in the Sitemap", + "sitemap-topics": "Site Haritası'nda görüntülenecek başlıkların sayısı", "clear-sitemap-cache": "Site haritası çerezlerini temizle", "view-sitemap": "Site haritasını gör" } \ No newline at end of file diff --git a/public/language/tr/error.json b/public/language/tr/error.json index f52f4aa8ea..7ff507c2f0 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Geçersiz Veri", + "invalid-json": "Geçersiz JSON", "not-logged-in": "Giriş yapmamış görünüyorsunuz.", "account-locked": "Hesabınız geçici olarak kitlendi", "search-requires-login": "Arama hesap gerektiriyor. Lütfen giriş yapın ya da kaydolun.", @@ -12,6 +13,7 @@ "invalid-title": "Geçersiz başlık!", "invalid-user-data": "Geçersiz Kullancı Verisi", "invalid-password": "Geçersiz Şifre", + "invalid-login-credentials": "Geçersiz kimlik bilgileri", "invalid-username-or-password": "Lütfen kullanıcı ismi ve parola girin.", "invalid-search-term": "Geçersiz arama", "csrf-invalid": "Büyük olasılıkla süresi dolmuş oturum nedeniyle girişinizi geçersiz kıldık. Lütfen tekrar deneyiniz.", diff --git a/public/language/tr/global.json b/public/language/tr/global.json index b1b6a2f06e..747f0ca087 100644 --- a/public/language/tr/global.json +++ b/public/language/tr/global.json @@ -103,5 +103,7 @@ "cookies.message": "Bu web sitesi en iyi deneyimi elde etmeniz amacıyla çerezlerden yararlanır.", "cookies.accept": "Anladım!", "cookies.learn_more": "Daha Fazla", - "edited": "Düzenlendi" + "edited": "Düzenlendi", + "disabled": "Devredışı", + "select": "Seç" } \ No newline at end of file diff --git a/public/language/tr/topic.json b/public/language/tr/topic.json index ad57a76331..104e10c766 100644 --- a/public/language/tr/topic.json +++ b/public/language/tr/topic.json @@ -14,6 +14,7 @@ "quote": "Alıntı", "reply": "Cevap", "replies_to_this_post": "%1 Cevap", + "one_reply_to_this_post": "1 Cevap", "last_reply_time": "Son cevap", "reply-as-topic": "İletiye Cevap Ver", "guest-login-reply": "Cevaplamak için giriş yapın", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Başlığı Aç", "thread_tools.move": "Başlığı Taşı", "thread_tools.move_all": "Hepsini Taşı", + "thread_tools.select_category": "Kategori Seç", "thread_tools.fork": "Başlığı Ayır", "thread_tools.delete": "Başlığı Sil", "thread_tools.delete-posts": "İletileri Sil", @@ -71,7 +73,6 @@ "post_restore_confirm": "Bu iletiyi gerçekten geri getirmek istiyor musun?", "post_purge_confirm": "Bu iletiyi temizlemek istediğinize eminmisiniz?", "load_categories": "Kategoriler Yükleniyor", - "disabled_categories_note": "Etkin Olmayan Kategoriler soluklaştırılır", "confirm_move": "Taşı", "confirm_fork": "Ayır", "bookmark": "Yer imi", @@ -83,7 +84,6 @@ "move_post": "İletiyi Taşı", "post_moved": "İleti taşındı!", "fork_topic": "Başlığı Ayır", - "topic_will_be_moved_to": "Bu konu kategorisine taşınacak", "fork_topic_instruction": "Ayırmak istediğiniz iletileri tıklayın", "fork_no_pids": "Hiç bir ileti seçilmedi!", "fork_pid_count": "%1 ileti(ler) seçildi", diff --git a/public/language/uk/admin/appearance/customise.json b/public/language/uk/admin/appearance/customise.json index e14527f2b6..57d0599759 100644 --- a/public/language/uk/admin/appearance/customise.json +++ b/public/language/uk/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Користувацький заголовок", "custom-header.description": "Уведіть власний HTML (JavaScript, мета теги, тощо), що буде додано до секції <head> вашого форуму.", - "custom-header.enable": "Увімкнути користувацький заголовок" + "custom-header.enable": "Увімкнути користувацький заголовок", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/uk/admin/development/info.json b/public/language/uk/admin/development/info.json index e70699c869..6830b7bed0 100644 --- a/public/language/uk/admin/development/info.json +++ b/public/language/uk/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Інформація - Ви знаходитесь на %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/uk/admin/general/languages.json b/public/language/uk/admin/general/languages.json index 4db88abb21..61f8e39b73 100644 --- a/public/language/uk/admin/general/languages.json +++ b/public/language/uk/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Налаштування мов", "description": "Мова за замовчуванням задає мову для всіх користувачів, що відвідують форум.
Кожен користувач може перевизначити мову в своїх налаштуваннях акаунта.", - "default-language": "Мова за замовчуванням" + "default-language": "Мова за замовчуванням", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/uk/admin/manage/categories.json b/public/language/uk/admin/manage/categories.json index d03162d48f..8b9831ec27 100644 --- a/public/language/uk/admin/manage/categories.json +++ b/public/language/uk/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Користувацький клас", "num-recent-replies": "Кількість свіжих відповідей", "ext-link": "Зовнішнє посилання", + "is-section": "Treat this category as a section", "upload-image": "Завантажити зображення", "delete-image": "Видалити", "category-image": "Зображення категорії", diff --git a/public/language/uk/admin/settings/user.json b/public/language/uk/admin/settings/user.json index cdcff8f963..83753d78f3 100644 --- a/public/language/uk/admin/settings/user.json +++ b/public/language/uk/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Кількість запрошень на користувача", "max-invites": "Кількість запрошень на користувача", "max-invites-help": "0 — без обмежень. Адміни отримуют необмежену кількість.
Працює лише з типом реєстрації \"По запрошенню\".", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Мінімальна довжина імені користувача", "max-username-length": "Максимальна довжина імені користувача", "min-password-length": "Мінімальна довжина пароля", diff --git a/public/language/uk/error.json b/public/language/uk/error.json index 0ae2e3a6f2..cdffc47872 100644 --- a/public/language/uk/error.json +++ b/public/language/uk/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Невірні дані", + "invalid-json": "Invalid JSON", "not-logged-in": "Не схоже, що ви увійшли в систему.", "account-locked": "Ваш акаунт тимчасово заблоковано", "search-requires-login": "Для пошуку потрібен акаунт — будь ласка, увійдіть чи зареєструйтесь.", @@ -12,6 +13,7 @@ "invalid-title": "Невірний заголовок!", "invalid-user-data": "Невірні користувацькі дані", "invalid-password": "Невірний пароль", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Вкажіть, будь ласка, ім'я користувача та пароль", "invalid-search-term": "Невірний пошуковий запит", "csrf-invalid": "Нам не вдалося вас пустити, ймовірно, через прострочену сесію. Будь ласка, спробуйте ще раз", diff --git a/public/language/uk/global.json b/public/language/uk/global.json index bab1461cc6..dbe083bac7 100644 --- a/public/language/uk/global.json +++ b/public/language/uk/global.json @@ -103,5 +103,7 @@ "cookies.message": "Цей сайт використовує куки, щоб ви отримали найкращий досвід при роботі з сайтом.", "cookies.accept": "Зрозуміло!", "cookies.learn_more": "Дізнатися більше", - "edited": "Відредаговано" + "edited": "Відредаговано", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/uk/topic.json b/public/language/uk/topic.json index b936e85ea5..5bfa66be4d 100644 --- a/public/language/uk/topic.json +++ b/public/language/uk/topic.json @@ -14,6 +14,7 @@ "quote": "Цитувати", "reply": "Відповісти", "replies_to_this_post": "%1 відповідей", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Остання відповідь", "reply-as-topic": "Відповісти темою", "guest-login-reply": "Увійти для відповіді", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Розблокувати тему", "thread_tools.move": "Перемістити тему", "thread_tools.move_all": "Перемістити всі", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Відгалужити тему", "thread_tools.delete": "Видалити тему", "thread_tools.delete-posts": "Видалити пости", @@ -71,7 +73,6 @@ "post_restore_confirm": "Ви точно бажаєте відновити цей пост?", "post_purge_confirm": "Ви точно бажаєте стерти цей пост?", "load_categories": "Завантаження категорій", - "disabled_categories_note": "Вимкнені категорії фарбуються в сірий", "confirm_move": "Перемістити", "confirm_fork": "Відгалужити", "bookmark": "Закладка", @@ -83,7 +84,6 @@ "move_post": "Перемістити пост", "post_moved": "Пост переміщено!", "fork_topic": "Відгалужити тему", - "topic_will_be_moved_to": "Цю тему буде переміщено в категорію", "fork_topic_instruction": "Тисніть пости які ви бажаєте відгалужити", "fork_no_pids": "Не вибрано жодного поста!", "fork_pid_count": "вибрано %1 пост(ів) ", diff --git a/public/language/vi/admin/advanced/database.json b/public/language/vi/admin/advanced/database.json index 7dd15423dc..27aaa79b67 100644 --- a/public/language/vi/admin/advanced/database.json +++ b/public/language/vi/admin/advanced/database.json @@ -17,7 +17,7 @@ "mongo.file-size": "Kích thước tập tin", "mongo.resident-memory": "Resident Memory", "mongo.virtual-memory": "Bộ nhớ ảo", - "mongo.mapped-memory": "Mapped Memory", + "mongo.mapped-memory": "Nhìn vào bộ nhớ ảo", "mongo.raw-info": "Thông tin MongoDB", "redis": "Redis", diff --git a/public/language/vi/admin/advanced/logs.json b/public/language/vi/admin/advanced/logs.json index b9de400e1c..945f128f34 100644 --- a/public/language/vi/admin/advanced/logs.json +++ b/public/language/vi/admin/advanced/logs.json @@ -1,7 +1,7 @@ { - "logs": "Logs", - "control-panel": "Logs Control Panel", - "reload": "Reload Logs", - "clear": "Clear Logs", - "clear-success": "Logs Cleared!" + "logs": "Nhật ký", + "control-panel": "Bảng điều khiển log", + "reload": "Tải lại log", + "clear": "Xóa các log", + "clear-success": "Các log đã được xóa!" } \ No newline at end of file diff --git a/public/language/vi/admin/appearance/customise.json b/public/language/vi/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/vi/admin/appearance/customise.json +++ b/public/language/vi/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/vi/admin/appearance/skins.json b/public/language/vi/admin/appearance/skins.json index 4db6fbdd8a..678229cd1c 100644 --- a/public/language/vi/admin/appearance/skins.json +++ b/public/language/vi/admin/appearance/skins.json @@ -1,9 +1,9 @@ { - "loading": "Loading Skins...", - "homepage": "Homepage", - "select-skin": "Select Skin", - "current-skin": "Current Skin", - "skin-updated": "Skin Updated", - "applied-success": "%1 skin was succesfully applied", - "revert-success": "Skin reverted to base colours" + "loading": "Đang tải giao diện ...", + "homepage": "Trang chủ", + "select-skin": "Chọn giao diện", + "current-skin": "Giao diện hiện tại", + "skin-updated": "Đã cập nhật giao diện", + "applied-success": "1% giao diện đã được sử dụng thành công", + "revert-success": "Đã trả giao diện về màu cơ bản" } \ No newline at end of file diff --git a/public/language/vi/admin/development/info.json b/public/language/vi/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/vi/admin/development/info.json +++ b/public/language/vi/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/vi/admin/general/languages.json b/public/language/vi/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/vi/admin/general/languages.json +++ b/public/language/vi/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/vi/admin/manage/categories.json b/public/language/vi/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/vi/admin/manage/categories.json +++ b/public/language/vi/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/vi/admin/settings/user.json b/public/language/vi/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/vi/admin/settings/user.json +++ b/public/language/vi/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/vi/error.json b/public/language/vi/error.json index a833820d5c..1d4265594e 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -1,5 +1,6 @@ { "invalid-data": "Dữ liệu không hợp lệ", + "invalid-json": "Invalid JSON", "not-logged-in": "Có vẻ bạn chưa đăng nhập.", "account-locked": "Tài khoản của bạn đang tạm thời bị khóa", "search-requires-login": "Bạn cần phải có tài khoản để tìm kiếm - vui lòng đăng nhập hoặc đăng ký.", @@ -12,6 +13,7 @@ "invalid-title": "Tiêu đề không hợp lệ!", "invalid-user-data": "Dữ liệu tài khoản không hợp lệ", "invalid-password": "Mật khẩu không hợp lệ", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Xin hãy nhập cả tên đăng nhập và mật khẩu", "invalid-search-term": "Từ khóa không hợp lệ", "csrf-invalid": "Hệ thống không cho phép bạn đăng nhập, có vẻ như phiên đăng nhập cũ đã hết hạn. Hãy thử đăng nhập lại", diff --git a/public/language/vi/global.json b/public/language/vi/global.json index 65db660c60..bca009cd8c 100644 --- a/public/language/vi/global.json +++ b/public/language/vi/global.json @@ -103,5 +103,7 @@ "cookies.message": "Trang web này sử dụng cookie để đảm bảo trải nghiệm tốt nhất cho người dùng", "cookies.accept": "Đã rõ!", "cookies.learn_more": "Xem thêm", - "edited": "Đã cập nhật" + "edited": "Đã cập nhật", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index 5c1cd238c7..d3379b29fd 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -14,6 +14,7 @@ "quote": "Trích dẫn", "reply": "Trả lời", "replies_to_this_post": "%1 trả lời", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Trả lời cuối cùng", "reply-as-topic": "Trả lời dưới dạng chủ đề", "guest-login-reply": "Hãy đăng nhập để trả lời", @@ -58,6 +59,7 @@ "thread_tools.unlock": "Mở khóa chủ đề", "thread_tools.move": "Chuyển chủ đề", "thread_tools.move_all": "Chuyển tất cả", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "Tạo bản sao chủ đề", "thread_tools.delete": "Xóa chủ đề", "thread_tools.delete-posts": "Xoá bài viết", @@ -71,7 +73,6 @@ "post_restore_confirm": "Bạn có chắc là muốn phục hồi bài gửi này không?", "post_purge_confirm": "Bạn có chắc muốn xóa hẳn bài này?", "load_categories": "Đang tải các phần mục", - "disabled_categories_note": "Các phần mục bị khóa đã được đánh xám", "confirm_move": "Di chuyển", "confirm_fork": "Tạo bảo sao", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "Chuyển bài gửi", "post_moved": "Đã chuyển bài gửi!", "fork_topic": "Tạo bản sao chủ đề", - "topic_will_be_moved_to": "Chủ đề này sẽ được chuyển tới phần mục", "fork_topic_instruction": "Chọn vào bài gửi mà bạn muốn fork", "fork_no_pids": "Chưa chọn bài gửi nào!", "fork_pid_count": "%1 bài viết(s) đã được gửi", diff --git a/public/language/zh-CN/admin/appearance/customise.json b/public/language/zh-CN/admin/appearance/customise.json index e609257a2f..cc9050e9e0 100644 --- a/public/language/zh-CN/admin/appearance/customise.json +++ b/public/language/zh-CN/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "自定义 Header", "custom-header.description": "请输入自定义的 HTML 代码 (如 JavaScript,Meta Tags 等),这些代码会被添加到论坛的 <head> 部分。", - "custom-header.enable": "启用自定义 Header" + "custom-header.enable": "启用自定义 Header", + + "custom-css.livereload": "启用实时重载", + "custom-css.livereload.description": "启用此功能可以在您点击保存时强制您帐户下的每个设备上的所有会话进行刷新" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/development/info.json b/public/language/zh-CN/admin/development/info.json index 0eba4d219c..46e955f539 100644 --- a/public/language/zh-CN/admin/development/info.json +++ b/public/language/zh-CN/admin/development/info.json @@ -1,12 +1,14 @@ { "you-are-on": "信息 - 你正在 %1:%2 上", + "nodes-responded": "%1个节点在%2ms内响应!", "host": "主机", "pid": "pid", "nodejs": "nodejs", "online": "在线", "git": "git", + "memory": "内存", "load": "载入", - "uptime": "正常运行时间", + "uptime": "运行时间", "registered": "已注册", "sockets": "接口", diff --git a/public/language/zh-CN/admin/general/dashboard.json b/public/language/zh-CN/admin/general/dashboard.json index b99b3b2bcf..300e651d89 100644 --- a/public/language/zh-CN/admin/general/dashboard.json +++ b/public/language/zh-CN/admin/general/dashboard.json @@ -5,14 +5,14 @@ "users": "用户", "posts": "发帖", "topics": "主题", - "page-views-seven": "Last 7 Days", - "page-views-thirty": "Last 30 Days", - "page-views-last-day": "Last 24 hours", - "page-views-custom": "Custom Date Range", - "page-views-custom-start": "Range Start", - "page-views-custom-end": "Range End", - "page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is YYYY-MM-DD", - "page-views-custom-error": "Please enter a valid date range in the format YYYY-MM-DD", + "page-views-seven": "最近7天", + "page-views-thirty": "最近30天", + "page-views-last-day": "最近24小时", + "page-views-custom": "自定义日期范围", + "page-views-custom-start": "范围开始", + "page-views-custom-end": "范围结束", + "page-views-custom-help": "输入您要查看的网页浏览日期范围。 如果没有日期选择器可用,则接受的格式是 YYYY-MM-DD", + "page-views-custom-error": "请输入 YYYY-MM-DD格式的有效日期范围 ", "stats.day": "日", "stats.week": "周", diff --git a/public/language/zh-CN/admin/general/languages.json b/public/language/zh-CN/admin/general/languages.json index f7cff12294..b8cb60203e 100644 --- a/public/language/zh-CN/admin/general/languages.json +++ b/public/language/zh-CN/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "语言设置", "description": "默认语言会决定所有用户的语言设定。
单一用户可以各自在帐户设置中覆盖此项设定。", - "default-language": "默认语言" + "default-language": "默认语言", + "auto-detect": "自动检测游客的语言设置" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/manage/categories.json b/public/language/zh-CN/admin/manage/categories.json index 77aa93ec68..babc97bb9f 100644 --- a/public/language/zh-CN/admin/manage/categories.json +++ b/public/language/zh-CN/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "自定义 Class", "num-recent-replies": "最近回复数", "ext-link": "外部链接", + "is-section": "将该板块作为段落", "upload-image": "上传图片", "delete-image": "移除", "category-image": "版块图片", diff --git a/public/language/zh-CN/admin/manage/groups.json b/public/language/zh-CN/admin/manage/groups.json index 4fee922f44..dc2149046f 100644 --- a/public/language/zh-CN/admin/manage/groups.json +++ b/public/language/zh-CN/admin/manage/groups.json @@ -1,7 +1,7 @@ { "name": "群组名", "description": "群组描述", - "member-count": "Member Count", + "member-count": "成员数", "system": "系统群组", "edit": "编辑", "search-placeholder": "搜索", diff --git a/public/language/zh-CN/admin/settings/advanced.json b/public/language/zh-CN/admin/settings/advanced.json index 4cc5377e39..8616bab339 100644 --- a/public/language/zh-CN/admin/settings/advanced.json +++ b/public/language/zh-CN/admin/settings/advanced.json @@ -6,7 +6,7 @@ "headers.allow-from": "设置 ALLOW-FROM 来放置 NodeBB 于 iFrame 中", "headers.powered-by": "自定义由 NodeBB 发送的 \"Powered By\" 头部 ", "headers.acao": "Access-Control-Allow-Origin", - "headers.acao-help": "To deny access to all sites, leave empty", + "headers.acao-help": "要拒绝所有网站,请留空", "headers.acam": "Access-Control-Allow-Methods", "headers.acah": "Access-Control-Allow-Headers", "traffic-management": "流量管理", diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json index 6f77d26602..c693e256f2 100644 --- a/public/language/zh-CN/admin/settings/post.json +++ b/public/language/zh-CN/admin/settings/post.json @@ -29,8 +29,8 @@ "unread": "未读设置", "unread.cutoff": "未读截止天数", "unread.min-track-last": "跟踪最后阅读之前的主题最小帖子", - "recent": "Recent Settings", - "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", + "recent": "最近设置", + "recent.categoryFilter.disable": "禁用对 /recent 页面上忽略类别中的主题进行过滤", "signature": "签名设置", "signature.disable": "禁用签名", "signature.no-links": "禁用签名中的链接", diff --git a/public/language/zh-CN/admin/settings/user.json b/public/language/zh-CN/admin/settings/user.json index d147da2ccc..7fb7f68bf3 100644 --- a/public/language/zh-CN/admin/settings/user.json +++ b/public/language/zh-CN/admin/settings/user.json @@ -34,10 +34,12 @@ "registration.max-invites": "每个用户最大邀请数", "max-invites": "每个用户最大邀请数", "max-invites-help": "无限制填 0 。管理员没有邀请限制
仅在邀请制时可用", + "invite-expiration": "邀请过期", + "invite-expiration-help": "邀请在#日过期。", "min-username-length": "最小用户名长度", "max-username-length": "最大用户名长度", "min-password-length": "最小密码长度", - "min-password-strength": "Minimum Password Strength", + "min-password-strength": "最小密码强度", "max-about-me-length": "自我介绍的最大长度", "terms-of-use": "论坛使用条款 (留空即可禁用)", "user-search": "用户搜索", diff --git a/public/language/zh-CN/email.json b/public/language/zh-CN/email.json index f247ee4b1f..75ff03cf23 100644 --- a/public/language/zh-CN/email.json +++ b/public/language/zh-CN/email.json @@ -32,9 +32,9 @@ "notif.post.unsub.info": "根据您的订阅设置,为您发送此回帖提醒。", "test.text1": "这是一封测试邮件,用来验证 NodeBB 的邮件配置是否设置正确。", "unsub.cta": "点击这里修改这些设置", - "banned.subject": "You have been banned from %1", - "banned.text1": "The user %1 has been banned from %2.", - "banned.text2": "This ban will last until %1.", - "banned.text3": "This is the reason why you have been banned:", + "banned.subject": "您已被封禁从 %1", + "banned.text1": "用户 %1 已被封禁从 %2.", + "banned.text2": "封禁将持续到 %1.", + "banned.text3": "这是您被封禁的原因:", "closing": "谢谢!" } \ No newline at end of file diff --git a/public/language/zh-CN/error.json b/public/language/zh-CN/error.json index 89f130c7ff..5b913ab1c6 100644 --- a/public/language/zh-CN/error.json +++ b/public/language/zh-CN/error.json @@ -1,5 +1,6 @@ { "invalid-data": "无效数据", + "invalid-json": "无效 JSON", "not-logged-in": "您还没有登录。", "account-locked": "您的帐号已被临时锁定", "search-requires-login": "搜索功能仅限会员使用 - 请先登录或者注册。", @@ -12,6 +13,7 @@ "invalid-title": "无效标题!", "invalid-user-data": "无效用户数据", "invalid-password": "无效密码", + "invalid-login-credentials": "无效登录凭证", "invalid-username-or-password": "请确认用户名和密码", "invalid-search-term": "无效的搜索关键字", "csrf-invalid": "可能是由于会话过期,登录失败。请重试。", @@ -30,7 +32,7 @@ "password-too-long": "密码太长", "user-banned": "用户已禁止", "user-banned-reason": "抱歉,此帐号已经被封禁 (原因:%1)", - "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", + "user-banned-reason-until": "抱歉,此帐户已被封禁,直到%1(原因:%2)", "user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!", "blacklisted-ip": "对不起,您的 IP 地址已被社区禁用。如果您认为这是一个错误,请与管理员联系。", "ban-expiry-missing": "请提供此次禁言结束日期", @@ -105,7 +107,7 @@ "chat-disabled": "聊天系统已关闭", "too-many-messages": "您发送了太多消息,请稍等片刻。", "invalid-chat-message": "无效的聊天信息", - "chat-message-too-long": "Chat messages can not be longer than %1 characters.", + "chat-message-too-long": "聊天消息不能超过 %1  个字符。", "cant-edit-chat-message": "您不能编辑这条信息", "cant-remove-last-user": "您不能移除这个用户", "cant-delete-chat-message": "您不允许删除这条消息", diff --git a/public/language/zh-CN/global.json b/public/language/zh-CN/global.json index 6555431231..8cc6f51c61 100644 --- a/public/language/zh-CN/global.json +++ b/public/language/zh-CN/global.json @@ -103,5 +103,7 @@ "cookies.message": "此网站使用 Cookies 以保障您在我们网站的最佳体验。", "cookies.accept": "知道了!", "cookies.learn_more": "了解更多", - "edited": "已编辑" + "edited": "已编辑", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/zh-CN/modules.json b/public/language/zh-CN/modules.json index 32241beaba..484962b740 100644 --- a/public/language/zh-CN/modules.json +++ b/public/language/zh-CN/modules.json @@ -20,7 +20,7 @@ "chat.three_months": "3个月", "chat.delete_message_confirm": "确认删除此消息吗?", "chat.add-users-to-room": "向此聊天室中添加成员", - "chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?", + "chat.confirm-chat-with-dnd-user": "该用户将其状态设置为DnD(请勿打扰)。 你还想和他们聊天吗?", "composer.compose": "编写帮助", "composer.show_preview": "显示预览", "composer.hide_preview": "隐藏预览", diff --git a/public/language/zh-CN/topic.json b/public/language/zh-CN/topic.json index cbd70048ef..84cfc9924f 100644 --- a/public/language/zh-CN/topic.json +++ b/public/language/zh-CN/topic.json @@ -13,7 +13,8 @@ "notify_me": "此主题有新回复时通知我", "quote": "引用", "reply": "回复", - "replies_to_this_post": "回复 %1", + "replies_to_this_post": "%1 条回复", + "one_reply_to_this_post": "1 条回复", "last_reply_time": "最后回复", "reply-as-topic": "在新帖中回复", "guest-login-reply": "登录后回复", @@ -58,6 +59,7 @@ "thread_tools.unlock": "解锁主题", "thread_tools.move": "移动主题", "thread_tools.move_all": "移动全部", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "分割主题", "thread_tools.delete": "删除主题", "thread_tools.delete-posts": "删除这些帖子", @@ -71,7 +73,6 @@ "post_restore_confirm": "确定恢复此帖吗?", "post_purge_confirm": "确认清除此回帖吗?", "load_categories": "正在载入版块", - "disabled_categories_note": "停用的版块为灰色", "confirm_move": "移动", "confirm_fork": "分割", "bookmark": "书签", @@ -83,7 +84,6 @@ "move_post": "移动帖子", "post_moved": "帖子已移走!", "fork_topic": "分割主题", - "topic_will_be_moved_to": "此主题将被移动到版块", "fork_topic_instruction": "点击将分割的帖子", "fork_no_pids": "未选中帖子!", "fork_pid_count": "选择了 %1 个帖子", diff --git a/public/language/zh-CN/user.json b/public/language/zh-CN/user.json index 8e8c90ada7..7c43d425b3 100644 --- a/public/language/zh-CN/user.json +++ b/public/language/zh-CN/user.json @@ -60,7 +60,7 @@ "username_taken_workaround": "您申请的用户名已被占用,所以我们稍作更改。您现在的用户名是 %1", "password_same_as_username": "您的密码与用户名相同,请选择另外的密码。", "password_same_as_email": "您的密码与邮箱相同,请选择另外的密码。", - "weak_password": "Weak password.", + "weak_password": "密码强度低。", "upload_picture": "上传头像", "upload_a_picture": "上传头像", "remove_uploaded_picture": "删除已上传的头像", diff --git a/public/language/zh-TW/admin/appearance/customise.json b/public/language/zh-TW/admin/appearance/customise.json index 767d443e29..5095f7a937 100644 --- a/public/language/zh-TW/admin/appearance/customise.json +++ b/public/language/zh-TW/admin/appearance/customise.json @@ -5,5 +5,8 @@ "custom-header": "Custom Header", "custom-header.description": "Enter custom HTML here (ex. JavaScript, Meta Tags, etc.), which will be appended to the <head> section of your forum's markup.", - "custom-header.enable": "Enable Custom Header" + "custom-header.enable": "Enable Custom Header", + + "custom-css.livereload": "Enable Live Reload", + "custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save" } \ No newline at end of file diff --git a/public/language/zh-TW/admin/development/info.json b/public/language/zh-TW/admin/development/info.json index b2768ca212..0a4ae6fe2c 100644 --- a/public/language/zh-TW/admin/development/info.json +++ b/public/language/zh-TW/admin/development/info.json @@ -1,10 +1,12 @@ { "you-are-on": "Info - You are on %1:%2", + "nodes-responded": "%1 nodes responded within %2ms!", "host": "host", "pid": "pid", "nodejs": "nodejs", "online": "online", "git": "git", + "memory": "memory", "load": "load", "uptime": "uptime", diff --git a/public/language/zh-TW/admin/general/languages.json b/public/language/zh-TW/admin/general/languages.json index da45cade2c..bdd57849b3 100644 --- a/public/language/zh-TW/admin/general/languages.json +++ b/public/language/zh-TW/admin/general/languages.json @@ -1,5 +1,6 @@ { "language-settings": "Language Settings", "description": "The default language determines the language settings for all users who are visiting your forum.
Individual users can override the default language on their account settings page.", - "default-language": "Default Language" + "default-language": "Default Language", + "auto-detect": "Auto Detect Language Setting for Guests" } \ No newline at end of file diff --git a/public/language/zh-TW/admin/manage/categories.json b/public/language/zh-TW/admin/manage/categories.json index 7e2a5ce12e..871affe560 100644 --- a/public/language/zh-TW/admin/manage/categories.json +++ b/public/language/zh-TW/admin/manage/categories.json @@ -10,6 +10,7 @@ "custom-class": "Custom Class", "num-recent-replies": "# of Recent Replies", "ext-link": "External Link", + "is-section": "Treat this category as a section", "upload-image": "Upload Image", "delete-image": "Remove", "category-image": "Category Image", diff --git a/public/language/zh-TW/admin/settings/user.json b/public/language/zh-TW/admin/settings/user.json index b8f51c9288..fa8049c3a9 100644 --- a/public/language/zh-TW/admin/settings/user.json +++ b/public/language/zh-TW/admin/settings/user.json @@ -34,6 +34,8 @@ "registration.max-invites": "Maximum Invitations per User", "max-invites": "Maximum Invitations per User", "max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"", + "invite-expiration": "Invite expiration", + "invite-expiration-help": "# of days invitations expire in.", "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", diff --git a/public/language/zh-TW/error.json b/public/language/zh-TW/error.json index 9ac4e1f494..7ce8059d6d 100644 --- a/public/language/zh-TW/error.json +++ b/public/language/zh-TW/error.json @@ -1,5 +1,6 @@ { "invalid-data": "無效的資料", + "invalid-json": "Invalid JSON", "not-logged-in": "你似乎還沒有登入喔!", "account-locked": "你的帳戶暫時被鎖定!", "search-requires-login": "進行搜尋需要有帳號 - 請先註冊或登入", @@ -12,6 +13,7 @@ "invalid-title": "無效的主題!", "invalid-user-data": "無效的使用者資料", "invalid-password": "無效的密碼", + "invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "請指定帳號和密碼", "invalid-search-term": "無效的搜索字詞", "csrf-invalid": "我們無法讓你登入,似乎是因為連線階段已到期。請再重試一次。", diff --git a/public/language/zh-TW/global.json b/public/language/zh-TW/global.json index b6f8f5d7d2..b1a7fbd6c3 100644 --- a/public/language/zh-TW/global.json +++ b/public/language/zh-TW/global.json @@ -103,5 +103,7 @@ "cookies.message": "This website uses cookies to ensure you get the best experience on our website.", "cookies.accept": "Got it!", "cookies.learn_more": "Learn More", - "edited": "Edited" + "edited": "Edited", + "disabled": "Disabled", + "select": "Select" } \ No newline at end of file diff --git a/public/language/zh-TW/topic.json b/public/language/zh-TW/topic.json index 292490d858..07b37ee21e 100644 --- a/public/language/zh-TW/topic.json +++ b/public/language/zh-TW/topic.json @@ -14,6 +14,7 @@ "quote": "引用", "reply": "回覆", "replies_to_this_post": "%1 Replies", + "one_reply_to_this_post": "1 Reply", "last_reply_time": "Last reply", "reply-as-topic": "回復為另一個新主題", "guest-login-reply": "登入以回覆", @@ -58,6 +59,7 @@ "thread_tools.unlock": "解除主題鎖定", "thread_tools.move": "移動主題", "thread_tools.move_all": "移動全部", + "thread_tools.select_category": "Select Category", "thread_tools.fork": "分叉主題", "thread_tools.delete": "刪除主題", "thread_tools.delete-posts": "刪除張貼", @@ -71,7 +73,6 @@ "post_restore_confirm": "你確定要還原這文章嗎?", "post_purge_confirm": "你確定要清除這個帖子嗎?", "load_categories": "版面載入中", - "disabled_categories_note": "停用的版面為灰色", "confirm_move": "移動", "confirm_fork": "作為主題", "bookmark": "Bookmark", @@ -83,7 +84,6 @@ "move_post": "移動文章", "post_moved": "文章已移動!", "fork_topic": "作為主題", - "topic_will_be_moved_to": "這個主題將會被移動到類別", "fork_topic_instruction": "點擊要作為主題的文章", "fork_no_pids": "尚未選擇文章!", "fork_pid_count": "%1 張貼已選定", diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index 8f713028ed..0e4c590d00 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -232,6 +232,11 @@ body { } } +.category-dropdown-menu { + max-height: 600px; + overflow-y: auto; +} + .table-reordering { tr:hover { cursor: move; diff --git a/public/less/admin/manage/categories.less b/public/less/admin/manage/categories.less index c0c0a544dc..28eb9c7ff9 100644 --- a/public/less/admin/manage/categories.less +++ b/public/less/admin/manage/categories.less @@ -12,8 +12,6 @@ div.categories { .row { margin-left: -15px; margin-right: -15px; - padding-bottom: 12px; - margin-bottom: 12px; } > li li:last-child { @@ -37,7 +35,6 @@ div.categories { li { min-height: @acp-line-height; margin: @acp-base-line 0; - margin-top: 28px; &.placeholder { border: 1px dashed #2196F3; @@ -56,21 +53,40 @@ div.categories { } } - .icon { - width: @acp-line-height; - height: @acp-line-height; + .toggle { + width: 24px; + height: 24px; border-radius: 50%; - line-height: @acp-line-height; + line-height: 24px; text-align: center; vertical-align: bottom; background-size: cover; float: left; - margin-right: @acp-margin; + margin-right: 0px; + cursor: pointer; + .fa { + font-size: 85%; + } + } + + .icon { + width: 24px; + height: 24px; + border-radius: 50%; + line-height: 24px; + text-align: center; + vertical-align: bottom; + background-size: cover; + float: left; + margin-right: 0px; cursor: move; + .fa { + font-size: 85%; + } } .information { - padding-left: 70px; + padding-left: 60px; } .category-header { diff --git a/public/src/admin/manage/categories.js b/public/src/admin/manage/categories.js index cb5d7e9975..2951481411 100644 --- a/public/src/admin/manage/categories.js +++ b/public/src/admin/manage/categories.js @@ -31,6 +31,12 @@ define('admin/manage/categories', ['vendor/jquery/serializeObject/jquery.ba-seri Categories.toggle([cid].concat(children), !disabled); return false; }); + + $('.categories').on('click', '.toggle', function () { + var el = $(this); + el.find('i').toggleClass('fa-minus').toggleClass('fa-plus'); + el.closest('[data-cid]').find('> ul[data-cid]').toggleClass('hidden'); + }); }; Categories.throwCreateModal = function () { diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index 1dbd31aaac..65ff87e683 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -7,7 +7,8 @@ define('admin/manage/category', [ 'admin/modules/colorpicker', 'autocomplete', 'translator', -], function (uploader, iconSelect, colorpicker, autocomplete, translator) { + 'categorySelector', +], function (uploader, iconSelect, colorpicker, autocomplete, translator, categorySelector) { var Category = {}; var modified_categories = {}; @@ -18,7 +19,7 @@ define('admin/manage/category', [ }); $('#category-selector').on('change', function () { - ajaxify.go('admin/manage/categories/' + $(this).val()); + ajaxify.go('admin/manage/categories/' + $(this).val() + window.location.hash); }); function enableColorPicker(idx, inputEl) { @@ -146,6 +147,10 @@ define('admin/manage/category', [ iconSelect.init($(this).find('i'), modified); }); + $('[type="checkbox"]').on('change', function () { + modified($(this)); + }); + $('button[data-action="setParent"], button[data-action="changeParent"]').on('click', Category.launchParentSelector); $('button[data-action="removeParent"]').on('click', function () { var payload = {}; @@ -172,8 +177,15 @@ define('admin/manage/category', [ var cid = ajaxify.data.category.cid; if (cid) { + var value; + if ($(el).is(':checkbox')) { + value = $(el).is(':checked') ? 1 : 0; + } else { + value = $(el).val(); + } + modified_categories[cid] = modified_categories[cid] || {}; - modified_categories[cid][$(el).attr('data-name')] = $(el).val(); + modified_categories[cid][$(el).attr('data-name')] = value; app.flags = app.flags || {}; app.flags._unsaved = true; @@ -286,47 +298,30 @@ define('admin/manage/category', [ }; Category.launchParentSelector = function () { - socket.emit('categories.get', function (err, categories) { - if (err) { - return app.alertError(err.message); - } + var categories = ajaxify.data.allCategories.filter(function (category) { + return category && !category.disabled && parseInt(category.cid, 10) !== parseInt(ajaxify.data.category.cid, 10); + }); - categories = categories.filter(function (category) { - return category && !category.disabled && parseInt(category.cid, 10) !== parseInt(ajaxify.data.category.cid, 10); - }); + selectCategoryModal(categories, function (parentCid) { + var payload = {}; - templates.parse('partials/category_list', { - categories: categories, - }, function (html) { - var modal = bootbox.dialog({ - message: html, - title: '[[admin/manage/categories:alert.set-parent-category]]', + payload[ajaxify.data.category.cid] = { + parentCid: parentCid, + }; + + socket.emit('admin.categories.update', payload, function (err) { + if (err) { + return app.alertError(err.message); + } + var parent = ajaxify.data.allCategories.filter(function (category) { + return category && parseInt(category.cid, 10) === parseInt(parentCid, 10); }); + parent = parent[0]; - modal.find('li[data-cid]').on('click', function () { - var parentCid = $(this).attr('data-cid'); - var payload = {}; - - payload[ajaxify.data.category.cid] = { - parentCid: parentCid, - }; - - socket.emit('admin.categories.update', payload, function (err) { - if (err) { - return app.alertError(err.message); - } - var parent = categories.filter(function (category) { - return category && parseInt(category.cid, 10) === parseInt(parentCid, 10); - }); - parent = parent[0]; - - modal.modal('hide'); - $('button[data-action="removeParent"]').parent().removeClass('hide'); - $('button[data-action="setParent"]').addClass('hide'); - var buttonHtml = ' ' + parent.name; - $('button[data-action="changeParent"]').html(buttonHtml).parent().removeClass('hide'); - }); - }); + $('button[data-action="removeParent"]').parent().removeClass('hide'); + $('button[data-action="setParent"]').addClass('hide'); + var buttonHtml = ' ' + parent.name; + $('button[data-action="changeParent"]').html(buttonHtml).parent().removeClass('hide'); }); }); }; @@ -407,37 +402,38 @@ define('admin/manage/category', [ }); }; - function selectCategoryModal(callback) { - socket.emit('admin.categories.getNames', function (err, categories) { - if (err) { - return app.alertError(err.message); - } - - templates.parse('admin/partials/categories/select-category', { - categories: categories, - }, function (html) { - translator.translate(html, function (html) { - var modal = bootbox.dialog({ - title: 'Select a Category', - message: html, - buttons: { - save: { - label: 'Copy', - className: 'btn-primary', - callback: submit, - }, + function selectCategoryModal(categories, callback) { + if (typeof categories === 'function') { + callback = categories; + categories = ajaxify.data.allCategories; + } + templates.parse('admin/partials/categories/select-category', { + categories: categories, + }, function (html) { + translator.translate(html, function (html) { + var modal = bootbox.dialog({ + title: '[[modules:composer.select_category]]', + message: html, + buttons: { + save: { + label: '[[global:select]]', + className: 'btn-primary', + callback: submit, }, - }); - - function submit() { - var formData = modal.find('form').serializeObject(); - callback(formData['select-cid']); - modal.modal('hide'); - return false; - } - - modal.find('form').on('submit', submit); + }, }); + categorySelector.init(modal.find('[component="category-selector"]')); + function submit(ev) { + ev.preventDefault(); + var selectedCategory = categorySelector.getSelectedCategory(); + if (selectedCategory) { + callback(selectedCategory.cid); + modal.modal('hide'); + } + return false; + } + + modal.find('form').on('submit', submit); }); }); } diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 93eb4791b0..9ca9052dc9 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -293,11 +293,21 @@ $(document).ready(function () { headers: { 'X-Return-To': app.previousUrl, }, - success: function (data) { + success: function (data, textStatus, xhr) { if (!data) { return; } + if (xhr.getResponseHeader('X-Redirect')) { + return callback({ + data: { + status: 302, + responseJSON: data, + }, + textStatus: 'error', + }); + } + ajaxify.data = data; data.config = config; @@ -395,6 +405,10 @@ $(document).ready(function () { } if (app.flags && app.flags.hasOwnProperty('_unsaved') && app.flags._unsaved === true) { + if (e.ctrlKey) { + return; + } + translator.translate('[[global:unsaved-changes]]', function (text) { bootbox.confirm(text, function (navigate) { if (navigate) { diff --git a/public/src/client/account/edit/username.js b/public/src/client/account/edit/username.js index 33d8ea99cd..f00e4d16fe 100644 --- a/public/src/client/account/edit/username.js +++ b/public/src/client/account/edit/username.js @@ -33,6 +33,8 @@ define('forum/account/edit/username', ['forum/account/header'], function (header var userslug = utils.slugify(userData.username); if (userData.username && userslug && parseInt(userData.uid, 10) === parseInt(app.user.uid, 10)) { $('[component="header/profilelink"]').attr('href', config.relative_path + '/user/' + userslug); + $('[component="header/profilelink/edit"]').attr('href', config.relative_path + '/user/' + userslug + '/edit'); + $('[component="header/profilelink/settings"]').attr('href', config.relative_path + '/user/' + userslug + '/settings'); $('[component="header/username"]').text(userData.username); $('[component="header/usericon"]').css('background-color', data['icon:bgColor']).text(data['icon:text']); } diff --git a/public/src/client/category.js b/public/src/client/category.js index 4cdd18ca2b..a91f32b03e 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -16,7 +16,7 @@ define('forum/category', [ var Category = {}; $(window).on('action:ajaxify.start', function (ev, data) { - if (ajaxify.currentPage !== data.url) { + if (data.url && !data.url.startsWith('category/')) { navigator.disable(); removeListeners(); @@ -113,11 +113,14 @@ define('forum/category', [ return bottomIndex; }; - $(window).on('action:ajaxify.contentLoaded', function () { + $(window).on('action:popstate', function () { if (ajaxify.data.template.category && ajaxify.data.cid) { var bookmarkIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark'); var clickedIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark:clicked'); + storage.removeItem('category:' + ajaxify.data.cid + ':bookmark'); + storage.removeItem('category:' + ajaxify.data.cid + ':bookmark:clicked'); + bookmarkIndex = Math.max(0, parseInt(bookmarkIndex, 10) || 0); clickedIndex = Math.max(0, parseInt(clickedIndex, 10) || 0); if (!parseInt(bookmarkIndex, 10)) { @@ -128,10 +131,10 @@ define('forum/category', [ var page = Math.ceil((parseInt(bookmarkIndex, 10) + 1) / config.topicsPerPage); if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { pagination.loadPage(page, function () { - Category.scrollToTopic(bookmarkIndex, clickedIndex, 400); + Category.scrollToTopic(bookmarkIndex, clickedIndex, 0); }); } else { - Category.scrollToTopic(bookmarkIndex, clickedIndex, 400); + Category.scrollToTopic(bookmarkIndex, clickedIndex, 0); } } else { if (bookmarkIndex === 0) { diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js index d01b908fc5..c32bae1e5e 100644 --- a/public/src/client/flags/detail.js +++ b/public/src/client/flags/detail.js @@ -44,6 +44,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator'], f }); FlagsList.enableFilterForm(); + FlagsList.enableChatButtons(); }; Flags.reloadNotes = function (notes) { diff --git a/public/src/client/login.js b/public/src/client/login.js index 078b7448f7..0ee61e48f7 100644 --- a/public/src/client/login.js +++ b/public/src/client/login.js @@ -35,8 +35,14 @@ define('forum/login', [], function () { headers: { 'x-csrf-token': config.csrf_token, }, - success: function (data) { - window.location.href = data + '?loggedin'; + success: function (returnTo) { + var pathname = utils.urlToLocation(returnTo).pathname; + + var params = utils.params({ url: returnTo }); + params.loggedin = true; + var qs = decodeURIComponent($.param(params)); + + window.location.href = pathname + '?' + qs; }, error: function (data) { if (data.status === 403 && data.responseText === 'Forbidden') { @@ -48,7 +54,7 @@ define('forum/login', [], function () { app.flags._sessionRefresh = false; // Select the entire password if that field has focus - if ($('#password:focus').size()) { + if ($('#password:focus').length) { $('#password').select(); } } diff --git a/public/src/client/notifications.js b/public/src/client/notifications.js index 04213499e3..88216fe61e 100644 --- a/public/src/client/notifications.js +++ b/public/src/client/notifications.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/notifications', ['components', 'notifications'], function (components, notifs) { +define('forum/notifications', ['components'], function (components) { var Notifications = {}; Notifications.init = function () { @@ -12,14 +12,6 @@ define('forum/notifications', ['components', 'notifications'], function (compone if (err) { return app.alertError(err); } - - socket.emit('notifications.getCount', function (err, count) { - if (err) { - return app.alertError(err.message); - } - - notifs.updateNotifCount(count); - }); }); }); @@ -32,7 +24,6 @@ define('forum/notifications', ['components', 'notifications'], function (compone } components.get('notifications/item').removeClass('unread'); - notifs.updateNotifCount(0); }); }); }; diff --git a/public/src/client/search.js b/public/src/client/search.js index e6691e626e..b9e0aff09e 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -7,8 +7,6 @@ define('forum/search', ['search', 'autocomplete', 'storage'], function (searchMo Search.init = function () { var searchQuery = $('#results').attr('data-search-query'); - $('#search-input').val(searchQuery); - var searchIn = $('#search-in'); searchIn.on('change', function () { @@ -19,7 +17,7 @@ define('forum/search', ['search', 'autocomplete', 'storage'], function (searchMo $('#advanced-search').off('submit').on('submit', function (e) { e.preventDefault(); - searchModule.query(getSearchData(), function () { + searchModule.query(getSearchDataFromDOM(), function () { $('#search-input').val(''); }); return false; @@ -32,7 +30,7 @@ define('forum/search', ['search', 'autocomplete', 'storage'], function (searchMo fillOutForm(); }; - function getSearchData() { + function getSearchDataFromDOM() { var form = $('#advanced-search'); var searchData = { in: $('#search-in').val(), @@ -52,6 +50,11 @@ define('forum/search', ['search', 'autocomplete', 'storage'], function (searchMo searchData.showAs = form.find('#show-as-topics').is(':checked') ? 'topics' : 'posts'; } + $(window).trigger('action:search.getSearchDataFromDOM', { + form: form, + data: searchData, + }); + return searchData; } @@ -62,12 +65,13 @@ define('forum/search', ['search', 'autocomplete', 'storage'], function (searchMo function fillOutForm() { var params = utils.params(); + var searchData = searchModule.getSearchPreferences(); var formData = utils.merge(searchData, params); if (formData) { - if (params.term) { - $('#search-input').val(params.term); + if (ajaxify.data.term) { + $('#search-input').val(ajaxify.data.term); } if (formData.in) { @@ -147,7 +151,7 @@ define('forum/search', ['search', 'autocomplete', 'storage'], function (searchMo function handleSavePreferences() { $('#save-preferences').on('click', function () { - storage.setItem('search-preferences', JSON.stringify(getSearchData())); + storage.setItem('search-preferences', JSON.stringify(getSearchDataFromDOM())); app.alertSuccess('[[search:search-preferences-saved]]'); return false; }); diff --git a/public/src/client/topic.js b/public/src/client/topic.js index c88af60237..08a707f029 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -23,16 +23,14 @@ define('forum/topic', [ Topic.replaceURLTimeout = 0; } - if (ajaxify.currentPage !== data.url) { + if (data.url && !data.url.startsWith('topic/')) { navigator.disable(); components.get('navbar/title').find('span').text('').hide(); app.removeAlert('bookmark'); events.removeListeners(); $(window).off('keydown', onKeyDown); - } - if (data.url && !data.url.startsWith('topic/')) { require(['search'], function (search) { if (search.topicDOM.active) { search.topicDOM.end(); @@ -144,11 +142,11 @@ define('forum/topic', [ function handleBookmark(tid) { // use the user's bookmark data if available, fallback to local if available var bookmark = ajaxify.data.bookmark || storage.getItem('topic:' + tid + ':bookmark'); - var postIndex = getPostIndex(); + var postIndex = ajaxify.data.postIndex; - if (postIndex && window.location.search.indexOf('page=') === -1) { - if (components.get('post/anchor', postIndex).length) { - return navigator.scrollToPostIndex(postIndex, true, 0); + if (postIndex > 0) { + if (components.get('post/anchor', postIndex - 1).length) { + return navigator.scrollToPostIndex(postIndex - 1, true, 0); } } else if (bookmark && (!config.usePagination || (config.usePagination && ajaxify.data.pagination.currentPage === 1)) && ajaxify.data.postcount > ajaxify.data.bookmarkThreshold) { app.alert({ @@ -169,22 +167,6 @@ define('forum/topic', [ } } - function getPostIndex() { - var parts = window.location.pathname.split('/'); - var lastPart = parts[parts.length - 1]; - if (lastPart && utils.isNumber(lastPart)) { - lastPart = Math.max(0, parseInt(lastPart, 10) - 1); - } else { - return 0; - } - - if (lastPart > 0 && !components.get('post/anchor', lastPart).length) { - return components.get('post/anchor').last().attr('name'); - } - - return lastPart; - } - function addBlockQuoteHandler() { components.get('topic').on('click', 'blockquote .toggle', function () { var blockQuote = $(this).parent('blockquote'); diff --git a/public/src/client/topic/move.js b/public/src/client/topic/move.js index 5ff9a02c49..0b969ef6b9 100644 --- a/public/src/client/topic/move.js +++ b/public/src/client/topic/move.js @@ -1,10 +1,10 @@ 'use strict'; -define('forum/topic/move', function () { +define('forum/topic/move', ['categorySelector'], function (categorySelector) { var Move = {}; var modal; - var selectedEl; + var selectedCategory; Move.init = function (tids, currentCid, onComplete) { Move.tids = tids; @@ -31,9 +31,7 @@ define('forum/topic/move', function () { modal.find('.modal-header h3').translateText('[[topic:move_topics]]'); } - modal.on('click', '.category-list li[data-cid]', function () { - selectCategory($(this)); - }); + categorySelector.init(modal.find('[component="category-selector"]'), onCategorySelected); modal.find('#move_thread_commit').on('click', onCommitClicked); @@ -42,60 +40,22 @@ define('forum/topic/move', function () { } function parseModal(categories, callback) { - templates.parse('partials/move_thread_modal', { categories: [] }, function (html) { - require(['translator'], function (translator) { - translator.translate(html, function (html) { - modal = $(html); - categories.forEach(function (category) { - if (!category.link) { - buildRecursive(modal.find('.category-list'), category, ''); - } - }); - callback(); - }); - }); + app.parseAndTranslate('partials/move_thread_modal', { categories: categories }, function (html) { + modal = $(html); + + callback(); }); } - function buildRecursive(parentEl, category, level) { - var categoryEl = $('
  • '); - - if (category.bgColor) { - categoryEl.css('background-color', category.bgColor); - } - if (category.color) { - categoryEl.css('color', category.color); - } - categoryEl.toggleClass('disabled', !!category.disabled); - categoryEl.attr('data-cid', category.cid); - categoryEl.attr('data-icon', category.icon); - categoryEl.attr('data-name', category.name); - categoryEl.html(' ' + category.name); - - parentEl.append(level); - parentEl.append(categoryEl); - parentEl.append('
    '); - - var indent = '      '; - category.children.forEach(function (childCategory) { - if (!childCategory.link) { - buildRecursive(parentEl, childCategory, indent + level); - } - }); - } - - function selectCategory(category) { - modal.find('#confirm-category-name').html(category.html()); - modal.find('#move-confirm').removeClass('hide'); - - selectedEl = category; + function onCategorySelected(category) { + selectedCategory = category; modal.find('#move_thread_commit').prop('disabled', false); } function onCommitClicked() { var commitEl = modal.find('#move_thread_commit'); - if (!commitEl.prop('disabled') && selectedEl.attr('data-cid')) { + if (!commitEl.prop('disabled') && selectedCategory && selectedCategory.cid) { commitEl.prop('disabled', true); moveTopics(); @@ -105,7 +65,7 @@ define('forum/topic/move', function () { function moveTopics() { socket.emit(Move.moveAll ? 'topics.moveAll' : 'topics.move', { tids: Move.tids, - cid: selectedEl.attr('data-cid'), + cid: selectedCategory.cid, currentCid: Move.currentCid, }, function (err) { modal.modal('hide'); @@ -114,7 +74,7 @@ define('forum/topic/move', function () { return app.alertError(err.message); } - app.alertSuccess('[[topic:topic_move_success, ' + selectedEl.attr('data-name') + ']] '); + app.alertSuccess('[[topic:topic_move_success, ' + selectedCategory.name + ']]'); if (typeof Move.onComplete === 'function') { Move.onComplete(); } diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index 707a9a5b0f..90b845538e 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -199,7 +199,7 @@ define('forum/topic/postTools', [ var selectedNode = getSelectedNode(); showStaleWarning(function () { - var username = getUserName(button); + var username = getUserSlug(button); if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) { username = ''; } @@ -231,7 +231,7 @@ define('forum/topic/postTools', [ var selectedNode = getSelectedNode(); showStaleWarning(function () { - var username = getUserName(button); + var username = getUserSlug(button); var toPid = getData(button, 'data-pid'); function quote(text) { @@ -284,7 +284,7 @@ define('forum/topic/postTools', [ selectedText = range.toString(); var postEl = $(content).parents('[component="post"]'); selectedPid = postEl.attr('data-pid'); - username = getUserName($(content)); + username = getUserSlug($(content)); range.detach(); } return { text: selectedText, pid: selectedPid, username: username }; @@ -309,22 +309,22 @@ define('forum/topic/postTools', [ return button.parents('[data-pid]').attr(data); } - function getUserName(button) { - var username = ''; + function getUserSlug(button) { + var slug = ''; var post = button.parents('[data-pid]'); if (button.attr('component') === 'topic/reply') { - return username; + return slug; } if (post.length) { - username = post.attr('data-username').replace(/\s/g, '-'); + slug = post.attr('data-userslug'); } if (post.length && post.attr('data-uid') !== '0') { - username = '@' + username; + slug = '@' + slug; } - return username; + return slug; } function togglePostDelete(button, tid) { diff --git a/public/src/modules/categorySelector.js b/public/src/modules/categorySelector.js new file mode 100644 index 0000000000..882206a42d --- /dev/null +++ b/public/src/modules/categorySelector.js @@ -0,0 +1,34 @@ +'use strict'; + + +define('categorySelector', function () { + var categorySelector = {}; + var selectedCategory; + var el; + categorySelector.init = function (_el, callback) { + callback = callback || function () {}; + el = _el; + selectedCategory = null; + el.on('click', '[data-cid]', function () { + var categoryEl = $(this); + categorySelector.selectCategory(categoryEl.attr('data-cid')); + callback(selectedCategory); + }); + }; + + categorySelector.getSelectedCategory = function () { + return selectedCategory; + }; + + categorySelector.selectCategory = function (cid) { + var categoryEl = el.find('[data-cid="' + cid + '"]'); + selectedCategory = { + cid: cid, + name: categoryEl.attr('data-name'), + }; + el.find('[component="category-selector-selected"]').html(categoryEl.find('[component="category-markup"]').html()); + }; + + return categorySelector; +}); + diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index e1cf620607..548e0ba47a 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -1,16 +1,38 @@ 'use strict'; -(function (exports) { - /* globals define, utils, config */ - - // export the class if we are in a Node-like system. - if (typeof module === 'object' && module.exports === exports) { - exports = module.exports/* = SemVer*/; +(function (factory) { + if (typeof module === 'object' && module.exports) { + var relative_path = require('nconf').get('relative_path'); + module.exports = factory(require('../utils'), require('templates.js'), require('string'), relative_path); + } else if (typeof define === 'function' && define.amd) { + define('helpers', ['string'], function (string) { + return factory(utils, templates, string, config.relative_path); + }); + } else { + window.helpers = factory(utils, templates, window.String, config.relative_path); } +}(function (utils, templates, S, relative_path) { + var helpers = { + displayMenuItem: displayMenuItem, + buildMetaTag: buildMetaTag, + buildLinkTag: buildLinkTag, + stringify: stringify, + escape: escape, + stripTags: stripTags, + generateCategoryBackground: generateCategoryBackground, + generateChildrenCategories: generateChildrenCategories, + generateTopicClass: generateTopicClass, + displayUserSearch: displayUserSearch, + membershipBtn: membershipBtn, + spawnPrivilegeStates: spawnPrivilegeStates, + localeToHTML: localeToHTML, + renderTopicImage: renderTopicImage, + renderDigestAvatar: renderDigestAvatar, + userAgentIcons: userAgentIcons, + register: register, + }; - var helpers = exports; - - helpers.displayMenuItem = function (data, index) { + function displayMenuItem(data, index) { var item = data.navigation[index]; if (!item) { return false; @@ -35,17 +57,17 @@ } return true; - }; + } - helpers.buildMetaTag = function (tag) { + function buildMetaTag(tag) { var name = tag.name ? 'name="' + tag.name + '" ' : ''; var property = tag.property ? 'property="' + tag.property + '" ' : ''; var content = tag.content ? 'content="' + tag.content.replace(/\n/g, ' ') + '" ' : ''; return '\n\t'; - }; + } - helpers.buildLinkTag = function (tag) { + function buildLinkTag(tag) { var link = tag.link ? 'link="' + tag.link + '" ' : ''; var rel = tag.rel ? 'rel="' + tag.rel + '" ' : ''; var type = tag.type ? 'type="' + tag.type + '" ' : ''; @@ -53,29 +75,22 @@ var sizes = tag.sizes ? 'sizes="' + tag.sizes + '" ' : ''; return '\n\t'; - }; + } - helpers.stringify = function (obj) { + function stringify(obj) { // Turns the incoming object into a JSON string return JSON.stringify(obj).replace(/&/gm, '&').replace(//gm, '>').replace(/"/g, '"'); - }; + } - helpers.escape = function (str) { - if (typeof utils !== 'undefined') { - return utils.escapeHTML(str); - } - return require('../utils').escapeHTML(str); - }; + function escape(str) { + return utils.escapeHTML(str); + } - helpers.stripTags = function (str) { - if (typeof window !== 'undefined' && window.S) { - return window.S(String(str)).stripTags().s; - } - var S = require('string'); + function stripTags(str) { return S(String(str)).stripTags().s; - }; + } - helpers.generateCategoryBackground = function (category) { + function generateCategoryBackground(category) { if (!category) { return ''; } @@ -97,16 +112,15 @@ } return style.join('; ') + ';'; - }; + } - helpers.generateChildrenCategories = function (category) { + function generateChildrenCategories(category) { var html = ''; - var relative_path = (typeof config !== 'undefined' ? config.relative_path : require('nconf').get('relative_path')); if (!category || !category.children || !category.children.length) { return html; } category.children.forEach(function (child) { - if (child) { + if (child && !child.isSection) { var link = child.link ? child.link : (relative_path + '/category/' + child.slug); html += '' + '' + @@ -117,9 +131,9 @@ }); html = html ? ('' + html + '') : html; return html; - }; + } - helpers.generateTopicClass = function (topic) { + function generateTopicClass(topic) { var style = []; if (topic.locked) { @@ -139,14 +153,14 @@ } return style.join(' '); - }; + } - helpers.displayUserSearch = function (data, allowGuestUserSearching) { + function displayUserSearch(data, allowGuestUserSearching) { return data.loggedIn || allowGuestUserSearching === 'true'; - }; + } // Groups helpers - helpers.membershipBtn = function (groupObj) { + function membershipBtn(groupObj) { if (groupObj.isMember && groupObj.name !== 'administrators') { return ''; } @@ -159,9 +173,9 @@ return ''; } return ''; - }; + } - helpers.spawnPrivilegeStates = function (member, privileges) { + function spawnPrivilegeStates(member, privileges) { var states = []; for (var priv in privileges) { if (privileges.hasOwnProperty(priv)) { @@ -174,21 +188,21 @@ return states.map(function (priv) { return ''; }).join(''); - }; + } - helpers.localeToHTML = function (locale, fallback) { + function localeToHTML(locale, fallback) { locale = locale || fallback || 'en-GB'; return locale.replace('_', '-'); - }; + } - helpers.renderTopicImage = function (topicObj) { + function renderTopicImage(topicObj) { if (topicObj.thumb) { return ''; } return ''; - }; + } - helpers.renderDigestAvatar = function (block) { + function renderDigestAvatar(block) { if (block.teaser) { if (block.teaser.user.picture) { return ''; @@ -199,9 +213,9 @@ return ''; } return '
    ' + block.user['icon:text'] + '
    '; - }; + } - helpers.userAgentIcons = function (data) { + function userAgentIcons(data) { var icons = ''; switch (data.platform) { @@ -251,29 +265,13 @@ } return icons; - }; - - exports.register = function () { - var templates; - - if (typeof module === 'object') { - templates = require('templates.js'); - } else { - templates = window.templates; - } + } + function register() { Object.keys(helpers).forEach(function (helperName) { templates.registerHelper(helperName, helpers[helperName]); }); - }; - - // export the class if we are in a Node-like system. - if (typeof module === 'object' && module.exports === exports) { - exports = module.exports/* = SemVer*/; - } else if (typeof define === 'function' && define.amd) { - // Use the define() function if we're in AMD land - define('helpers', exports); - } else if (typeof window === 'object') { - window.helpers = exports; } -}(typeof exports === 'object' ? exports : {})); + + return helpers; +})); diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 9ca7e94fa9..a7fd657cac 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -76,7 +76,7 @@ define('navigator', ['forum/pagination', 'components'], function (pagination, co index = 1; navigator.callback = null; navigator.selector = null; - $(window).off('scroll', navigator.update); + $(window).off('scroll', navigator.delayedUpdate); toggle(false); }; diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index ecbfaadd1b..928884b69a 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -10,29 +10,27 @@ define('notifications', ['sounds', 'translator', 'components'], function (sounds var notifContainer = components.get('notifications'); var notifTrigger = notifContainer.children('a'); var notifList = components.get('notifications/list'); - var notifIcon = components.get('notifications/icon'); - notifTrigger - .on('click', function (e) { - e.preventDefault(); - if (notifContainer.hasClass('open')) { - return; - } + notifTrigger.on('click', function (e) { + e.preventDefault(); + if (notifContainer.hasClass('open')) { + return; + } - Notifications.loadNotifications(notifList); - }); + Notifications.loadNotifications(notifList); + }); notifList.on('click', '[data-nid]', function () { var unread = $(this).hasClass('unread'); - var nid = $(this).attr('data-nid'); if (!unread) { return; } + var nid = $(this).attr('data-nid'); socket.emit('notifications.markRead', nid, function (err) { if (err) { return app.alertError(err.message); } - incrementNotifCount(-1); + if (unreadNotifs[nid]) { delete unreadNotifs[nid]; } @@ -52,7 +50,7 @@ define('notifications', ['sounds', 'translator', 'components'], function (sounds } liEl.toggleClass('unread'); - incrementNotifCount(unread ? -1 : 1); + if (unread && unreadNotifs[nid]) { delete unreadNotifs[nid]; } @@ -60,11 +58,6 @@ define('notifications', ['sounds', 'translator', 'components'], function (sounds return false; }); - function incrementNotifCount(delta) { - var count = parseInt(notifIcon.attr('data-content'), 10) + delta; - Notifications.updateNotifCount(count); - } - socket.on('event:new_notification', function (notifData) { // If a path is defined, show notif data, otherwise show generic data var payload = { @@ -164,7 +157,6 @@ define('notifications', ['sounds', 'translator', 'components'], function (sounds if (err) { app.alertError(err.message); } - Notifications.updateNotifCount(0); unreadNotifs = {}; }); }; diff --git a/public/src/modules/scrollStop.js b/public/src/modules/scrollStop.js index 82d2df4de8..0e07641cc3 100644 --- a/public/src/modules/scrollStop.js +++ b/public/src/modules/scrollStop.js @@ -16,7 +16,7 @@ define('scrollStop', function () { $(element).on('mousewheel', function (e) { var scrollTop = this.scrollTop; var scrollHeight = this.scrollHeight; - var elementHeight = this.getBoundingClientRect().height; + var elementHeight = Math.round(this.getBoundingClientRect().height); if ( (e.originalEvent.deltaY < 0 && scrollTop === 0) || // scroll up diff --git a/public/src/modules/search.js b/public/src/modules/search.js index 5b77ab7572..ce24bd4c40 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -74,6 +74,12 @@ define('search', ['navigator', 'translator', 'storage'], function (nav, translat if (data.showAs) { query.showAs = data.showAs; } + + $(window).trigger('action:search.createQueryString', { + query: query, + data: data, + }); + return decodeURIComponent($.param(query)); } diff --git a/public/src/modules/settings.js b/public/src/modules/settings.js index 93583d6ed9..5dfa5def2e 100644 --- a/public/src/modules/settings.js +++ b/public/src/modules/settings.js @@ -470,6 +470,9 @@ define('settings', function () { } } + // Save loaded settings into ajaxify.data for use client-side + ajaxify.data.settings = values; + $(formEl).deserialize(values); $(formEl).find('input[type="checkbox"]').each(function () { $(this).parents('.mdl-switch').toggleClass('is-checked', $(this).is(':checked')); @@ -510,6 +513,9 @@ define('settings', function () { // Remove unsaved flag to re-enable ajaxify app.flags._unsaved = false; + // Also save to local ajaxify.data + ajaxify.data.settings = values; + if (typeof callback === 'function') { callback(err); } else if (err) { diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index 5296e1eef4..465a240e4e 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -580,44 +580,18 @@ prepareDOM: function prepareDOM() { // Load the appropriate timeago locale file, // and correct NodeBB language codes to timeago codes, if necessary - var languageCode; - switch (config.userLang) { - case 'en-GB': - case 'en-US': - languageCode = 'en'; - break; + var languageCode = utils.userLangToTimeagoCode(config.userLang); - case 'fa-IR': - languageCode = 'fa'; - break; + adaptor.timeagoShort = assign({}, jQuery.timeago.settings.strings); - case 'pt-BR': - languageCode = 'pt-br'; - break; - - case 'nb': - languageCode = 'no'; - break; - - default: - languageCode = config.userLang; - break; - } - - jQuery.getScript(config.relative_path + '/assets/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').done(function () { - jQuery('.timeago').timeago(); - adaptor.timeagoShort = assign({}, jQuery.timeago.settings.strings); - - // Retrieve the shorthand timeago values as well - jQuery.getScript(config.relative_path + '/assets/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').done(function () { - // Switch back to long-form - adaptor.toggleTimeagoShorthand(); - }); + jQuery.getScript(config.relative_path + '/assets/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').done(function () { + // Switch back to long-form + adaptor.toggleTimeagoShorthand(); }); // Add directional code if necessary adaptor.translate('[[language:dir]]', function (value) { - if (value) { + if (value && !$('html').attr('data-dir')) { jQuery('html').css('direction', value).attr('data-dir', value); } }); diff --git a/public/src/utils.js b/public/src/utils.js index fe655c2e3b..6e7f0400d1 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -103,7 +103,16 @@ hasLanguageKey: function (input) { return utils.languageKeyRegex.test(input); }, - + userLangToTimeagoCode: function (userLang) { + var mapping = { + 'en-GB': 'en', + 'en-US': 'en', + 'fa-IR': 'fa', + 'pt-BR': 'pt-br', + nb: 'no', + }; + return mapping[userLang] || userLang; + }, // shallow objects merge merge: function () { var result = {}; diff --git a/src/batch.js b/src/batch.js index 9a0271f29b..3a91775025 100644 --- a/src/batch.js +++ b/src/batch.js @@ -30,17 +30,18 @@ exports.processSortedSet = function (setKey, process, options, callback) { }); } + options.batch = options.batch || DEFAULT_BATCH_SIZE; + // use the fast path if possible if (db.processSortedSet && typeof options.doneIf !== 'function' && !utils.isNumber(options.alwaysStartAt)) { - return db.processSortedSet(setKey, process, options.batch || DEFAULT_BATCH_SIZE, callback); + return db.processSortedSet(setKey, process, options, callback); } // custom done condition options.doneIf = typeof options.doneIf === 'function' ? options.doneIf : function () {}; - var batch = options.batch || DEFAULT_BATCH_SIZE; var start = 0; - var stop = batch; + var stop = options.batch; var done = false; async.whilst( @@ -48,24 +49,30 @@ exports.processSortedSet = function (setKey, process, options, callback) { return !done; }, function (next) { - db.getSortedSetRange(setKey, start, stop, function (err, ids) { - if (err) { - return next(err); - } - if (!ids.length || options.doneIf(start, stop, ids)) { - done = true; - return next(); - } - process(ids, function (err) { - if (err) { - return next(err); + async.waterfall([ + function (next) { + db.getSortedSetRange(setKey, start, stop, next); + }, + function (ids, _next) { + if (!ids.length || options.doneIf(start, stop, ids)) { + done = true; + return next(); } - start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : batch + 1; - stop = start + batch; + process(ids, function (err) { + _next(err); + }); + }, + function (next) { + start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : options.batch + 1; + stop = start + options.batch; - next(); - }); - }); + if (options.interval) { + setTimeout(next, options.interval); + } else { + next(); + } + }, + ], next); }, callback ); @@ -101,17 +108,21 @@ exports.processArray = function (array, process, options, callback) { done = true; return next(); } - process(currentBatch, function (err) { - if (err) { - return next(err); - } - start += batch; - if (options.interval) { - setTimeout(next, options.interval); - } else { - next(); - } - }); + async.waterfall([ + function (next) { + process(currentBatch, function (err) { + next(err); + }); + }, + function (next) { + start += batch; + if (options.interval) { + setTimeout(next, options.interval); + } else { + next(); + } + }, + ], next); }, function (err) { callback(err); diff --git a/src/categories.js b/src/categories.js index 787853faa7..4fd6ec3b1f 100644 --- a/src/categories.js +++ b/src/categories.js @@ -314,7 +314,18 @@ Categories.getTree = function (categories, parentCid) { return tree; }; -Categories.buildForSelect = function (uid, callback) { +Categories.buildForSelect = function (uid, privilege, callback) { + async.waterfall([ + function (next) { + Categories.getCategoriesByPrivilege('cid:0:children', uid, privilege, next); + }, + function (categories, next) { + Categories.buildForSelectCategories(categories, next); + }, + ], callback); +}; + +Categories.buildForSelectCategories = function (categories, callback) { function recursive(category, categoriesData, level) { if (category.link) { return; @@ -322,6 +333,7 @@ Categories.buildForSelect = function (uid, callback) { var bullet = level ? '• ' : ''; category.value = category.cid; + category.level = level; category.text = level + bullet + category.name; categoriesData.push(category); @@ -329,23 +341,17 @@ Categories.buildForSelect = function (uid, callback) { recursive(child, categoriesData, '    ' + level); }); } - async.waterfall([ - function (next) { - Categories.getCategoriesByPrivilege('cid:0:children', uid, 'read', next); - }, - function (categories, next) { - var categoriesData = []; - categories = categories.filter(function (category) { - return category && !category.link && !parseInt(category.parentCid, 10); - }); + var categoriesData = []; - categories.forEach(function (category) { - recursive(category, categoriesData, ''); - }); - next(null, categoriesData); - }, - ], callback); + categories = categories.filter(function (category) { + return category && !category.link && !parseInt(category.parentCid, 10); + }); + + categories.forEach(function (category) { + recursive(category, categoriesData, ''); + }); + callback(null, categoriesData); }; Categories.getIgnorers = function (cid, start, stop, callback) { diff --git a/src/categories/create.js b/src/categories/create.js index 326545d76c..e489bfa815 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -52,7 +52,18 @@ module.exports = function (Categories) { function (data, next) { category = data.category; - var defaultPrivileges = ['find', 'read', 'topics:read', 'topics:create', 'topics:reply', 'posts:edit', 'posts:delete', 'topics:delete', 'upload:post:image']; + var defaultPrivileges = [ + 'find', + 'read', + 'topics:read', + 'topics:create', + 'topics:reply', + 'topics:tag', + 'posts:edit', + 'posts:delete', + 'topics:delete', + 'upload:post:image', + ]; async.series([ async.apply(db.setObject, 'category:' + category.cid, category), diff --git a/src/categories/data.js b/src/categories/data.js index e3d9296c72..73c9300902 100644 --- a/src/categories/data.js +++ b/src/categories/data.js @@ -30,6 +30,7 @@ module.exports = function (Categories) { category.name = validator.escape(String(category.name || '')); category.disabled = category.hasOwnProperty('disabled') ? parseInt(category.disabled, 10) === 1 : undefined; + category.isSection = category.hasOwnProperty('isSection') ? parseInt(category.isSection, 10) === 1 : undefined; category.icon = category.icon || 'hidden'; if (category.hasOwnProperty('post_count')) { category.post_count = category.post_count || 0; diff --git a/src/controllers/accounts/notifications.js b/src/controllers/accounts/notifications.js index c6715ba144..d398003782 100644 --- a/src/controllers/accounts/notifications.js +++ b/src/controllers/accounts/notifications.js @@ -12,7 +12,7 @@ var notificationsController = module.exports; notificationsController.get = function (req, res, next) { var regularFilters = [ { name: '[[notifications:all]]', filter: '' }, - { name: '[[notifications:topics]]', filter: 'new-topic' }, + { name: '[[global:topics]]', filter: 'new-topic' }, { name: '[[notifications:replies]]', filter: 'new-reply' }, { name: '[[notifications:chat]]', filter: 'new-chat' }, { name: '[[notifications:follows]]', filter: 'follow' }, diff --git a/src/controllers/admin/appearance.js b/src/controllers/admin/appearance.js index 021733d417..65256f62ad 100644 --- a/src/controllers/admin/appearance.js +++ b/src/controllers/admin/appearance.js @@ -1,12 +1,9 @@ 'use strict'; -var appearanceController = {}; +var appearanceController = module.exports; appearanceController.get = function (req, res) { var term = req.params.term ? req.params.term : 'themes'; res.render('admin/appearance/' + term, {}); }; - - -module.exports = appearanceController; diff --git a/src/controllers/admin/cache.js b/src/controllers/admin/cache.js index dea5f0345b..1284be08be 100644 --- a/src/controllers/admin/cache.js +++ b/src/controllers/admin/cache.js @@ -1,6 +1,6 @@ 'use strict'; -var cacheController = {}; +var cacheController = module.exports; cacheController.get = function (req, res) { var postCache = require('../../posts/cache'); @@ -37,6 +37,3 @@ cacheController.get = function (req, res) { }, }); }; - - -module.exports = cacheController; diff --git a/src/controllers/admin/categories.js b/src/controllers/admin/categories.js index 3dae05a388..0b78912644 100644 --- a/src/controllers/admin/categories.js +++ b/src/controllers/admin/categories.js @@ -16,7 +16,7 @@ categoriesController.get = function (req, res, callback) { async.parallel({ category: async.apply(categories.getCategories, [req.params.category_id], req.user.uid), privileges: async.apply(privileges.categories.list, req.params.category_id), - allCategories: async.apply(categories.buildForSelect, req.uid), + allCategories: async.apply(categories.buildForSelect, req.uid, 'read'), }, next); }, function (data, next) { diff --git a/src/controllers/admin/dashboard.js b/src/controllers/admin/dashboard.js index 70149e580e..8b1751d2a6 100644 --- a/src/controllers/admin/dashboard.js +++ b/src/controllers/admin/dashboard.js @@ -7,76 +7,79 @@ var db = require('../../database'); var meta = require('../../meta'); var plugins = require('../../plugins'); -var dashboardController = {}; - +var dashboardController = module.exports; dashboardController.get = function (req, res, next) { - async.parallel({ - stats: function (next) { - getStats(next); - }, - notices: function (next) { - var notices = [ - { - done: !meta.reloadRequired, - doneText: '[[admin/general/dashboard:restart-not-required]]', - notDoneText: '[[admin/general/dashboard:restart-required]]', + async.waterfall([ + function (next) { + async.parallel({ + stats: function (next) { + getStats(next); }, - { - done: plugins.hasListeners('filter:search.query'), - doneText: '[[admin/general/dashboard:search-plugin-installed]]', - notDoneText: '[[admin/general/dashboard:search-plugin-not-installed]]', - tooltip: '[[admin/general/dashboard:search-plugin-tooltip]]', - link: '/admin/extend/plugins', + notices: function (next) { + var notices = [ + { + done: !meta.reloadRequired, + doneText: '[[admin/general/dashboard:restart-not-required]]', + notDoneText: '[[admin/general/dashboard:restart-required]]', + }, + { + done: plugins.hasListeners('filter:search.query'), + doneText: '[[admin/general/dashboard:search-plugin-installed]]', + notDoneText: '[[admin/general/dashboard:search-plugin-not-installed]]', + tooltip: '[[admin/general/dashboard:search-plugin-tooltip]]', + link: '/admin/extend/plugins', + }, + ]; + + if (global.env !== 'production') { + notices.push({ + done: false, + notDoneText: '[[admin/general/dashboard:running-in-development]]', + }); + } + + plugins.fireHook('filter:admin.notices', notices, next); }, - ]; - - if (global.env !== 'production') { - notices.push({ - done: false, - notDoneText: '[[admin/general/dashboard:running-in-development]]', - }); - } - - plugins.fireHook('filter:admin.notices', notices, next); + }, next); }, - }, function (err, results) { - if (err) { - return next(err); - } - res.render('admin/general/dashboard', { - version: nconf.get('version'), - notices: results.notices, - stats: results.stats, - }); - }); + function (results) { + res.render('admin/general/dashboard', { + version: nconf.get('version'), + notices: results.notices, + stats: results.stats, + }); + }, + ], next); }; function getStats(callback) { - async.parallel([ + async.waterfall([ function (next) { - getStatsForSet('ip:recent', 'uniqueIPCount', next); + async.parallel([ + function (next) { + getStatsForSet('ip:recent', 'uniqueIPCount', next); + }, + function (next) { + getStatsForSet('users:joindate', 'userCount', next); + }, + function (next) { + getStatsForSet('posts:pid', 'postCount', next); + }, + function (next) { + getStatsForSet('topics:tid', 'topicCount', next); + }, + ], next); }, - function (next) { - getStatsForSet('users:joindate', 'userCount', next); - }, - function (next) { - getStatsForSet('posts:pid', 'postCount', next); - }, - function (next) { - getStatsForSet('topics:tid', 'topicCount', next); - }, - ], function (err, results) { - if (err) { - return callback(err); - } - results[0].name = '[[admin/general/dashboard:unique-visitors]]'; - results[1].name = '[[admin/general/dashboard:users]]'; - results[2].name = '[[admin/general/dashboard:posts]]'; - results[3].name = '[[admin/general/dashboard:topics]]'; + function (results, next) { + results[0].name = '[[admin/general/dashboard:unique-visitors]]'; + results[1].name = '[[admin/general/dashboard:users]]'; + results[2].name = '[[admin/general/dashboard:posts]]'; + results[3].name = '[[admin/general/dashboard:topics]]'; - callback(null, results); - }); + next(null, results); + }, + ], callback); } function getStatsForSet(set, field, callback) { @@ -108,5 +111,3 @@ function getGlobalField(field, callback) { callback(err, parseInt(count, 10) || 0); }); } - -module.exports = dashboardController; diff --git a/src/controllers/admin/database.js b/src/controllers/admin/database.js index 9ce8a3c00f..efec771ee6 100644 --- a/src/controllers/admin/database.js +++ b/src/controllers/admin/database.js @@ -3,33 +3,32 @@ var async = require('async'); var nconf = require('nconf'); -var databaseController = {}; - +var databaseController = module.exports; databaseController.get = function (req, res, next) { - async.parallel({ - redis: function (next) { - if (nconf.get('redis')) { - var rdb = require('../../database/redis'); - rdb.info(rdb.client, next); - } else { - next(); - } + async.waterfall([ + function (next) { + async.parallel({ + redis: function (next) { + if (nconf.get('redis')) { + var rdb = require('../../database/redis'); + rdb.info(rdb.client, next); + } else { + next(); + } + }, + mongo: function (next) { + if (nconf.get('mongo')) { + var mdb = require('../../database/mongo'); + mdb.info(mdb.client, next); + } else { + next(); + } + }, + }, next); }, - mongo: function (next) { - if (nconf.get('mongo')) { - var mdb = require('../../database/mongo'); - mdb.info(mdb.client, next); - } else { - next(); - } + function (results) { + res.render('admin/advanced/database', results); }, - }, function (err, results) { - if (err) { - return next(err); - } - res.render('admin/advanced/database', results); - }); + ], next); }; - -module.exports = databaseController; diff --git a/src/controllers/admin/errors.js b/src/controllers/admin/errors.js index 5a00e95537..55f790edab 100644 --- a/src/controllers/admin/errors.js +++ b/src/controllers/admin/errors.js @@ -6,33 +6,28 @@ var json2csv = require('json-2-csv').json2csv; var meta = require('../../meta'); var analytics = require('../../analytics'); -var errorsController = {}; +var errorsController = module.exports; errorsController.get = function (req, res, next) { - async.parallel({ - 'not-found': async.apply(meta.errors.get, true), - analytics: async.apply(analytics.getErrorAnalytics), - }, function (err, data) { - if (err) { - return next(err); - } - - res.render('admin/advanced/errors', data); - }); + async.waterfall([ + function (next) { + async.parallel({ + 'not-found': async.apply(meta.errors.get, true), + analytics: async.apply(analytics.getErrorAnalytics), + }, next); + }, + function (data) { + res.render('admin/advanced/errors', data); + }, + ], next); }; errorsController.export = function (req, res, next) { async.waterfall([ async.apply(meta.errors.get, false), async.apply(json2csv), - ], function (err, csv) { - if (err) { - return next(err); - } - - res.set('Content-Type', 'text/csv').set('Content-Disposition', 'attachment; filename="404.csv"').send(csv); - }); + function (csv) { + res.set('Content-Type', 'text/csv').set('Content-Disposition', 'attachment; filename="404.csv"').send(csv); + }, + ], next); }; - - -module.exports = errorsController; diff --git a/src/controllers/admin/events.js b/src/controllers/admin/events.js index 97838d2266..f6988ef72e 100644 --- a/src/controllers/admin/events.js +++ b/src/controllers/admin/events.js @@ -6,8 +6,7 @@ var db = require('../../database'); var events = require('../../events'); var pagination = require('../../pagination'); -var eventsController = {}; - +var eventsController = module.exports; eventsController.get = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; @@ -15,27 +14,26 @@ eventsController.get = function (req, res, next) { var start = (page - 1) * itemsPerPage; var stop = start + itemsPerPage - 1; - async.parallel({ - eventCount: function (next) { - db.sortedSetCard('events:time', next); + async.waterfall([ + function (next) { + async.parallel({ + eventCount: function (next) { + db.sortedSetCard('events:time', next); + }, + events: function (next) { + events.getEvents(start, stop, next); + }, + }, next); }, - events: function (next) { - events.getEvents(start, stop, next); + function (results) { + var pageCount = Math.max(1, Math.ceil(results.eventCount / itemsPerPage)); + + res.render('admin/advanced/events', { + events: results.events, + pagination: pagination.create(page, pageCount), + next: 20, + }); }, - }, function (err, results) { - if (err) { - return next(err); - } - - var pageCount = Math.max(1, Math.ceil(results.eventCount / itemsPerPage)); - - res.render('admin/advanced/events', { - events: results.events, - pagination: pagination.create(page, pageCount), - next: 20, - }); - }); + ], next); }; - -module.exports = eventsController; diff --git a/src/controllers/admin/groups.js b/src/controllers/admin/groups.js index 5e7dd1e78f..b8d9291474 100644 --- a/src/controllers/admin/groups.js +++ b/src/controllers/admin/groups.js @@ -7,7 +7,7 @@ var groups = require('../../groups'); var meta = require('../../meta'); var pagination = require('../../pagination'); -var groupsController = {}; +var groupsController = module.exports; groupsController.list = function (req, res, next) { var page = parseInt(req.query.page, 10) || 1; @@ -16,7 +16,7 @@ groupsController.list = function (req, res, next) { async.waterfall([ function (next) { - db.getSortedSetRevRange('groups:createtime', 0, -1, next); + db.getSortedSetRange('groups:createtime', 0, -1, next); }, function (groupNames, next) { groupNames = groupNames.filter(function (name) { @@ -30,20 +30,14 @@ groupsController.list = function (req, res, next) { groupNames = groupNames.slice(start, stop + 1); groups.getGroupsData(groupNames, next); }, - function (groupData, next) { - next(null, { groups: groupData, pagination: pagination.create(page, pageCount) }); + function (groupData) { + res.render('admin/manage/groups', { + groups: groupData, + pagination: pagination.create(page, pageCount), + yourid: req.uid, + }); }, - ], function (err, data) { - if (err) { - return next(err); - } - - res.render('admin/manage/groups', { - groups: data.groups, - pagination: data.pagination, - yourid: req.uid, - }); - }); + ], next); }; groupsController.get = function (req, res, callback) { @@ -58,13 +52,12 @@ groupsController.get = function (req, res, callback) { } groups.get(groupName, { uid: req.uid, truncateUserList: true, userListCount: 20 }, next); }, - ], function (err, group) { - if (err) { - return callback(err); - } - group.isOwner = true; - res.render('admin/manage/group', { group: group, allowPrivateGroups: parseInt(meta.config.allowPrivateGroups, 10) === 1 }); - }); + function (group) { + group.isOwner = true; + res.render('admin/manage/group', { + group: group, + allowPrivateGroups: parseInt(meta.config.allowPrivateGroups, 10) === 1, + }); + }, + ], callback); }; - -module.exports = groupsController; diff --git a/src/controllers/admin/homepage.js b/src/controllers/admin/homepage.js index 1450283847..bc0971622f 100644 --- a/src/controllers/admin/homepage.js +++ b/src/controllers/admin/homepage.js @@ -7,8 +7,7 @@ var categories = require('../../categories'); var privileges = require('../../privileges'); var plugins = require('../../plugins'); -var homePageController = {}; - +var homePageController = module.exports; homePageController.get = function (req, res, next) { async.waterfall([ @@ -28,39 +27,29 @@ homePageController.get = function (req, res, next) { name: 'Category: ' + category.name, }; }); - next(null, categoryData); + + plugins.fireHook('filter:homepage.get', { routes: [ + { + route: 'categories', + name: 'Categories', + }, + { + route: 'recent', + name: 'Recent', + }, + { + route: 'popular', + name: 'Popular', + }, + ].concat(categoryData) }, next); }, - ], function (err, categoryData) { - if (err || !categoryData) { - categoryData = []; - } - - plugins.fireHook('filter:homepage.get', { routes: [ - { - route: 'categories', - name: 'Categories', - }, - { - route: 'recent', - name: 'Recent', - }, - { - route: 'popular', - name: 'Popular', - }, - ].concat(categoryData) }, function (err, data) { - if (err) { - return next(err); - } - + function (data) { data.routes.push({ route: '', name: 'Custom', }); res.render('admin/general/homepage', data); - }); - }); + }, + ], next); }; - -module.exports = homePageController; diff --git a/src/controllers/admin/languages.js b/src/controllers/admin/languages.js index e2d848ddae..987c1d70cc 100644 --- a/src/controllers/admin/languages.js +++ b/src/controllers/admin/languages.js @@ -1,26 +1,27 @@ 'use strict'; +var async = require('async'); + var languages = require('../../languages'); var meta = require('../../meta'); -var languagesController = {}; - +var languagesController = module.exports; languagesController.get = function (req, res, next) { - languages.list(function (err, languages) { - if (err) { - return next(err); - } + async.waterfall([ + function (next) { + languages.list(next); + }, + function (languages) { + languages.forEach(function (language) { + language.selected = language.code === (meta.config.defaultLang || 'en-GB'); + }); - languages.forEach(function (language) { - language.selected = language.code === (meta.config.defaultLang || 'en-GB'); - }); - - res.render('admin/general/languages', { - languages: languages, - autoDetectLang: parseInt(meta.config.autoDetectLang, 10) === 1, - }); - }); + res.render('admin/general/languages', { + languages: languages, + autoDetectLang: parseInt(meta.config.autoDetectLang, 10) === 1, + }); + }, + ], next); }; -module.exports = languagesController; diff --git a/src/controllers/admin/logger.js b/src/controllers/admin/logger.js index 0e8006bbeb..4d8991e88c 100644 --- a/src/controllers/admin/logger.js +++ b/src/controllers/admin/logger.js @@ -1,9 +1,7 @@ 'use strict'; -var loggerController = {}; +var loggerController = module.exports; loggerController.get = function (req, res) { res.render('admin/development/logger', {}); }; - -module.exports = loggerController; diff --git a/src/controllers/admin/logs.js b/src/controllers/admin/logs.js index c2c5166dd7..1765cfc352 100644 --- a/src/controllers/admin/logs.js +++ b/src/controllers/admin/logs.js @@ -1,21 +1,23 @@ 'use strict'; +var async = require('async'); var validator = require('validator'); + var meta = require('../../meta'); - -var logsController = {}; +var logsController = module.exports; logsController.get = function (req, res, next) { - meta.logs.get(function (err, logs) { - if (err) { - return next(err); - } - - res.render('admin/advanced/logs', { - data: validator.escape(logs), - }); - }); + async.waterfall([ + function (next) { + meta.logs.get(next); + }, + function (logs) { + res.render('admin/advanced/logs', { + data: validator.escape(logs), + }); + }, + ], next); }; diff --git a/src/controllers/admin/plugins.js b/src/controllers/admin/plugins.js index 4e8e1a415e..b04458edae 100644 --- a/src/controllers/admin/plugins.js +++ b/src/controllers/admin/plugins.js @@ -3,54 +3,54 @@ var async = require('async'); var plugins = require('../../plugins'); -var pluginsController = {}; +var pluginsController = module.exports; pluginsController.get = function (req, res, next) { - async.parallel({ - compatible: function (next) { - plugins.list(function (err, plugins) { - if (err || !Array.isArray(plugins)) { - plugins = []; - } + async.waterfall([ + function (next) { + async.parallel({ + compatible: function (next) { + plugins.list(function (err, plugins) { + if (err || !Array.isArray(plugins)) { + plugins = []; + } - next(null, plugins); + next(null, plugins); + }); + }, + all: function (next) { + plugins.list(false, function (err, plugins) { + if (err || !Array.isArray(plugins)) { + plugins = []; + } + + next(null, plugins); + }); + }, + }, next); + }, + function (payload) { + var compatiblePkgNames = payload.compatible.map(function (pkgData) { + return pkgData.name; + }); + + res.render('admin/extend/plugins', { + installed: payload.compatible.filter(function (plugin) { + return plugin.installed; + }), + upgradeCount: payload.compatible.reduce(function (count, current) { + if (current.installed && current.outdated) { + count += 1; + } + return count; + }, 0), + download: payload.compatible.filter(function (plugin) { + return !plugin.installed; + }), + incompatible: payload.all.filter(function (plugin) { + return compatiblePkgNames.indexOf(plugin.name) === -1; + }), }); }, - all: function (next) { - plugins.list(false, function (err, plugins) { - if (err || !Array.isArray(plugins)) { - plugins = []; - } - - next(null, plugins); - }); - }, - }, function (err, payload) { - if (err) { - return next(err); - } - var compatiblePkgNames = payload.compatible.map(function (pkgData) { - return pkgData.name; - }); - - res.render('admin/extend/plugins', { - installed: payload.compatible.filter(function (plugin) { - return plugin.installed; - }), - upgradeCount: payload.compatible.reduce(function (count, current) { - if (current.installed && current.outdated) { - count += 1; - } - return count; - }, 0), - download: payload.compatible.filter(function (plugin) { - return !plugin.installed; - }), - incompatible: payload.all.filter(function (plugin) { - return compatiblePkgNames.indexOf(plugin.name) === -1; - }), - }); - }); + ], next); }; - -module.exports = pluginsController; diff --git a/src/controllers/admin/rewards.js b/src/controllers/admin/rewards.js index 56c5ed3cd0..37b49929bb 100644 --- a/src/controllers/admin/rewards.js +++ b/src/controllers/admin/rewards.js @@ -1,15 +1,18 @@ 'use strict'; -var rewardsController = {}; +var async = require('async'); + +var rewardsController = module.exports; rewardsController.get = function (req, res, next) { - require('../../rewards/admin').get(function (err, data) { - if (err) { - return next(err); - } - - res.render('admin/extend/rewards', data); - }); + async.waterfall([ + function (next) { + require('../../rewards/admin').get(next); + }, + function (data) { + res.render('admin/extend/rewards', data); + }, + ], next); }; diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index 7d1db3e73e..97d9b17262 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -36,32 +36,30 @@ function renderEmail(req, res, next) { async.map(emails, function (email, next) { var path = email.replace(emailsPath, '').substr(1).replace('.tpl', ''); - fs.readFile(email, function (err, original) { - if (err) { - return next(err); - } + async.waterfall([ + function (next) { + fs.readFile(email, next); + }, + function (original, next) { + var text = meta.config['email:custom:' + path] ? meta.config['email:custom:' + path] : original.toString(); - var text = meta.config['email:custom:' + path] ? meta.config['email:custom:' + path] : original.toString(); - - next(null, { - path: path, - fullpath: email, - text: text, - original: original.toString(), - }); - }); + next(null, { + path: path, + fullpath: email, + text: text, + original: original.toString(), + }); + }, + ], next); }, next); }, - ], function (err, emails) { - if (err) { - return next(err); - } - - res.render('admin/settings/email', { - emails: emails, - sendable: emails.filter(function (email) { - return email.path.indexOf('_plaintext') === -1 && email.path.indexOf('partials') === -1; - }), - }); - }); + function (emails) { + res.render('admin/settings/email', { + emails: emails, + sendable: emails.filter(function (email) { + return email.path.indexOf('_plaintext') === -1 && email.path.indexOf('partials') === -1; + }), + }); + }, + ], next); } diff --git a/src/controllers/admin/sounds.js b/src/controllers/admin/sounds.js index 164bf4a427..c0ad374a55 100644 --- a/src/controllers/admin/sounds.js +++ b/src/controllers/admin/sounds.js @@ -1,9 +1,11 @@ 'use strict'; +var async = require('async'); + var plugins = require('../../plugins'); var meta = require('../../meta'); -var soundsController = {}; +var soundsController = module.exports; soundsController.get = function (req, res, next) { var types = [ @@ -11,37 +13,36 @@ soundsController.get = function (req, res, next) { 'chat-incoming', 'chat-outgoing', ]; - meta.configs.getFields(types, function (err, settings) { - if (err) { - return next(err); - } + async.waterfall([ + function (next) { + meta.configs.getFields(types, next); + }, + function (settings) { + settings = settings || {}; - settings = settings || {}; + var output = {}; - var output = {}; + types.forEach(function (type) { + var soundpacks = plugins.soundpacks.map(function (pack) { + var sounds = Object.keys(pack.sounds).map(function (soundName) { + var value = pack.name + ' | ' + soundName; + return { + name: soundName, + value: value, + selected: value === settings[type], + }; + }); - types.forEach(function (type) { - var soundpacks = plugins.soundpacks.map(function (pack) { - var sounds = Object.keys(pack.sounds).map(function (soundName) { - var value = pack.name + ' | ' + soundName; return { - name: soundName, - value: value, - selected: value === settings[type], + name: pack.name, + sounds: sounds, }; }); - return { - name: pack.name, - sounds: sounds, - }; + output[type + '-sound'] = soundpacks; }); - output[type + '-sound'] = soundpacks; - }); - - res.render('admin/general/sounds', output); - }); + res.render('admin/general/sounds', output); + }, + ], next); }; - -module.exports = soundsController; diff --git a/src/controllers/admin/tags.js b/src/controllers/admin/tags.js index f586e5f70c..3fec3915ec 100644 --- a/src/controllers/admin/tags.js +++ b/src/controllers/admin/tags.js @@ -1,18 +1,18 @@ 'use strict'; +var async = require('async'); + var topics = require('../../topics'); -var tagsController = {}; +var tagsController = module.exports; tagsController.get = function (req, res, next) { - topics.getTags(0, 199, function (err, tags) { - if (err) { - return next(err); - } - - res.render('admin/manage/tags', { tags: tags }); - }); + async.waterfall([ + function (next) { + topics.getTags(0, 199, next); + }, + function (tags) { + res.render('admin/manage/tags', { tags: tags }); + }, + ], next); }; - - -module.exports = tagsController; diff --git a/src/controllers/admin/themes.js b/src/controllers/admin/themes.js index 94fdf43746..850feb201d 100644 --- a/src/controllers/admin/themes.js +++ b/src/controllers/admin/themes.js @@ -1,25 +1,45 @@ 'use strict'; var path = require('path'); +var fs = require('fs'); +var async = require('async'); + var file = require('../../file'); -var themesController = {}; +var themesController = module.exports; + +var defaultScreenshotPath = path.join(__dirname, '../../../public/images/themes/default.png'); themesController.get = function (req, res, next) { - var themeDir = path.join(__dirname, '../../../node_modules/' + req.params.theme); - file.exists(themeDir, function (err, exists) { - if (err || !exists) { - return next(err); - } + var themeDir = path.join(__dirname, '../../../node_modules', req.params.theme); + var themeConfigPath = path.join(themeDir, 'theme.json'); + var screenshotPath; + async.waterfall([ + function (next) { + file.exists(themeConfigPath, next); + }, + function (exists, next) { + if (!exists) { + return next(Error('invalid-data')); + } - var themeConfig = require(path.join(themeDir, 'theme.json')); - var screenshotPath = path.join(themeDir, themeConfig.screenshot); - if (themeConfig.screenshot && file.existsSync(screenshotPath)) { - res.sendFile(screenshotPath); - } else { - res.sendFile(path.join(__dirname, '../../../public/images/themes/default.png')); - } - }); + fs.readFile(themeConfigPath, next); + }, + function (themeConfig, next) { + try { + themeConfig = JSON.parse(themeConfig); + next(null, themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath); + } catch (e) { + next(e); + } + }, + function (_screenshotPath, next) { + screenshotPath = _screenshotPath; + file.exists(screenshotPath, next); + }, + function (exists) { + res.sendFile(exists ? screenshotPath : defaultScreenshotPath); + }, + ], next); }; -module.exports = themesController; diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 7dd08994cc..d454b287a1 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -10,7 +10,7 @@ var pagination = require('../../pagination'); var events = require('../../events'); var plugins = require('../../plugins'); -var usersController = {}; +var usersController = module.exports; var userFields = ['uid', 'username', 'userslug', 'email', 'postcount', 'joindate', 'banned', 'reputation', 'picture', 'flags', 'lastonline', 'email:confirmed']; @@ -63,57 +63,59 @@ usersController.registrationQueue = function (req, res, next) { var stop = start + itemsPerPage - 1; var invitations; - async.parallel({ - registrationQueueCount: function (next) { - db.sortedSetCard('registration:queue', next); - }, - users: function (next) { - user.getRegistrationQueue(start, stop, next); - }, - customHeaders: function (next) { - plugins.fireHook('filter:admin.registrationQueue.customHeaders', { headers: [] }, next); - }, - invites: function (next) { - async.waterfall([ - function (next) { - user.getAllInvites(next); + async.waterfall([ + function (next) { + async.parallel({ + registrationQueueCount: function (next) { + db.sortedSetCard('registration:queue', next); }, - function (_invitations, next) { - invitations = _invitations; - async.map(invitations, function (invites, next) { - user.getUserField(invites.uid, 'username', next); - }, next); + users: function (next) { + user.getRegistrationQueue(start, stop, next); }, - function (usernames, next) { - invitations.forEach(function (invites, index) { - invites.username = usernames[index]; - }); - async.map(invitations, function (invites, next) { - async.map(invites.invitations, user.getUsernameByEmail, next); - }, next); + customHeaders: function (next) { + plugins.fireHook('filter:admin.registrationQueue.customHeaders', { headers: [] }, next); }, - function (usernames, next) { - invitations.forEach(function (invites, index) { - invites.invitations = invites.invitations.map(function (email, i) { - return { - email: email, - username: usernames[index][i] === '[[global:guest]]' ? '' : usernames[index][i], - }; - }); - }); - next(null, invitations); + invites: function (next) { + async.waterfall([ + function (next) { + user.getAllInvites(next); + }, + function (_invitations, next) { + invitations = _invitations; + async.map(invitations, function (invites, next) { + user.getUserField(invites.uid, 'username', next); + }, next); + }, + function (usernames, next) { + invitations.forEach(function (invites, index) { + invites.username = usernames[index]; + }); + async.map(invitations, function (invites, next) { + async.map(invites.invitations, user.getUsernameByEmail, next); + }, next); + }, + function (usernames, next) { + invitations.forEach(function (invites, index) { + invites.invitations = invites.invitations.map(function (email, i) { + return { + email: email, + username: usernames[index][i] === '[[global:guest]]' ? '' : usernames[index][i], + }; + }); + }); + next(null, invitations); + }, + ], next); }, - ], next); + }, next); }, - }, function (err, data) { - if (err) { - return next(err); - } - var pageCount = Math.max(1, Math.ceil(data.registrationQueueCount / itemsPerPage)); - data.pagination = pagination.create(page, pageCount); - data.customHeaders = data.customHeaders.headers; - res.render('admin/manage/registration', data); - }); + function (data) { + var pageCount = Math.max(1, Math.ceil(data.registrationQueueCount / itemsPerPage)); + data.pagination = pagination.create(page, pageCount); + data.customHeaders = data.customHeaders.headers; + res.render('admin/manage/registration', data); + }, + ], next); }; function getUsers(set, section, min, max, req, res, next) { @@ -123,47 +125,48 @@ function getUsers(set, section, min, max, req, res, next) { var stop = start + resultsPerPage - 1; var byScore = min !== undefined && max !== undefined; - async.parallel({ - count: function (next) { - if (byScore) { - db.sortedSetCount(set, min, max, next); - } else if (set === 'users:banned' || set === 'users:notvalidated') { - db.sortedSetCard(set, next); - } else { - db.getObjectField('global', 'userCount', next); - } - }, - users: function (next) { - async.waterfall([ - function (next) { + async.waterfall([ + function (next) { + async.parallel({ + count: function (next) { if (byScore) { - db.getSortedSetRevRangeByScore(set, start, resultsPerPage, max, min, next); + db.sortedSetCount(set, min, max, next); + } else if (set === 'users:banned' || set === 'users:notvalidated') { + db.sortedSetCard(set, next); } else { - user.getUidsFromSet(set, start, stop, next); + db.getObjectField('global', 'userCount', next); } }, - function (uids, next) { - user.getUsersWithFields(uids, userFields, req.uid, next); + users: function (next) { + async.waterfall([ + function (next) { + if (byScore) { + db.getSortedSetRevRangeByScore(set, start, resultsPerPage, max, min, next); + } else { + user.getUidsFromSet(set, start, stop, next); + } + }, + function (uids, next) { + user.getUsersWithFields(uids, userFields, req.uid, next); + }, + ], next); }, - ], next); + }, next); }, - }, function (err, results) { - if (err) { - return next(err); - } - - results.users = results.users.filter(function (user) { - user.email = validator.escape(String(user.email || '')); - return user && parseInt(user.uid, 10); - }); - var data = { - users: results.users, - page: page, - pageCount: Math.max(1, Math.ceil(results.count / resultsPerPage)), - }; - data[section] = true; - render(req, res, data); - }); + function (results) { + results.users = results.users.filter(function (user) { + user.email = validator.escape(String(user.email || '')); + return user && parseInt(user.uid, 10); + }); + var data = { + users: results.users, + page: page, + pageCount: Math.max(1, Math.ceil(results.count / resultsPerPage)), + }; + data[section] = true; + render(req, res, data); + }, + ], next); } function render(req, res, data) { @@ -185,15 +188,14 @@ usersController.getCSV = function (req, res, next) { uid: req.user.uid, ip: req.ip, }); - - user.getUsersCSV(function (err, data) { - if (err) { - return next(err); - } - res.attachment('users.csv'); - res.setHeader('Content-Type', 'text/csv'); - res.end(data); - }); + async.waterfall([ + function (next) { + user.getUsersCSV(next); + }, + function (data) { + res.attachment('users.csv'); + res.setHeader('Content-Type', 'text/csv'); + res.end(data); + }, + ], next); }; - -module.exports = usersController; diff --git a/src/controllers/admin/widgets.js b/src/controllers/admin/widgets.js index 889fa1dcc6..15e75a0f68 100644 --- a/src/controllers/admin/widgets.js +++ b/src/controllers/admin/widgets.js @@ -1,16 +1,16 @@ 'use strict'; -var widgetsController = {}; +var async = require('async'); + +var widgetsController = module.exports; widgetsController.get = function (req, res, next) { - require('../../widgets/admin').get(function (err, data) { - if (err) { - return next(err); - } - - res.render('admin/extend/widgets', data); - }); + async.waterfall([ + function (next) { + require('../../widgets/admin').get(next); + }, + function (data) { + res.render('admin/extend/widgets', data); + }, + ], next); }; - - -module.exports = widgetsController; diff --git a/src/controllers/category.js b/src/controllers/category.js index cb5b08f9a3..96e3d8c2bf 100644 --- a/src/controllers/category.js +++ b/src/controllers/category.js @@ -22,6 +22,7 @@ categoryController.get = function (req, res, callback) { var pageCount = 1; var userPrivileges; var settings; + var rssToken; if ((req.params.topic_index && !utils.isNumber(req.params.topic_index)) || !utils.isNumber(cid)) { return callback(); @@ -39,10 +40,14 @@ categoryController.get = function (req, res, callback) { userSettings: function (next) { user.getSettings(req.uid, next); }, + rssToken: function (next) { + user.auth.getFeedToken(req.uid, next); + }, }, next); }, function (results, next) { userPrivileges = results.privileges; + rssToken = results.rssToken; if (!results.categoryData.slug || (results.categoryData && parseInt(results.categoryData.disabled, 10) === 1)) { return callback(); @@ -149,15 +154,15 @@ categoryController.get = function (req, res, callback) { categoryData.description = translator.escape(categoryData.description); categoryData.privileges = userPrivileges; categoryData.showSelect = categoryData.privileges.editable; + categoryData.rssFeedUrl = nconf.get('url') + '/category/' + categoryData.cid + '.rss'; + if (parseInt(req.uid, 10)) { + categories.markAsRead([cid], req.uid); + categoryData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken; + } addTags(categoryData, res); - if (parseInt(req.uid, 10)) { - categories.markAsRead([cid], req.uid); - } - categoryData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; - categoryData.rssFeedUrl = nconf.get('relative_path') + '/category/' + categoryData.cid + '.rss'; categoryData.title = translator.escape(categoryData.name); pageCount = Math.max(1, Math.ceil(categoryData.topic_count / settings.topicsPerPage)); categoryData.pagination = pagination.create(currentPage, pageCount, req.query); @@ -220,7 +225,7 @@ function addTags(categoryData, res) { { rel: 'alternate', type: 'application/rss+xml', - href: nconf.get('url') + '/category/' + categoryData.cid + '.rss', + href: categoryData.rssFeedUrl, }, { rel: 'up', diff --git a/src/controllers/errors.js b/src/controllers/errors.js index 99614f9e87..177bcaa769 100644 --- a/src/controllers/errors.js +++ b/src/controllers/errors.js @@ -45,7 +45,7 @@ exports.handleErrors = function (err, req, res, next) { // eslint-disable-line n var status = parseInt(err.status, 10); if ((status === 302 || status === 308) && err.path) { - return res.locals.isAPI ? res.status(status).json(err.path) : res.redirect(err.path); + return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(err.path); } winston.error(req.path + '\n', err.stack); diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 9ee0ebc0d9..732652aa38 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -53,7 +53,7 @@ helpers.notAllowed = function (req, res, error) { helpers.redirect = function (res, url) { if (res.locals.isAPI) { - res.status(308).json(url); + res.set('X-Redirect', encodeURI(url)).status(200).json(url); } else { res.redirect(nconf.get('relative_path') + encodeURI(url)); } @@ -65,12 +65,12 @@ helpers.buildCategoryBreadcrumbs = function (cid, callback) { async.whilst(function () { return parseInt(cid, 10); }, function (next) { - categories.getCategoryFields(cid, ['name', 'slug', 'parentCid', 'disabled'], function (err, data) { + categories.getCategoryFields(cid, ['name', 'slug', 'parentCid', 'disabled', 'isSection'], function (err, data) { if (err) { return next(err); } - if (!parseInt(data.disabled, 10)) { + if (!parseInt(data.disabled, 10) && !parseInt(data.isSection, 10)) { breadcrumbs.unshift({ text: validator.escape(String(data.name)), url: nconf.get('relative_path') + '/category/' + data.slug, diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 32e416a20d..98990e97ba 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -59,7 +59,7 @@ modsController.flags.list = function (req, res, next) { async.parallel({ flags: async.apply(flags.list, filters, req.uid), analytics: async.apply(analytics.getDailyStatsForSet, 'analytics:flags', Date.now(), 30), - categories: async.apply(categories.buildForSelect, req.uid), + categories: async.apply(categories.buildForSelect, req.uid, 'read'), }, next); }, function (data) { diff --git a/src/controllers/search.js b/src/controllers/search.js index bf818a39d5..f02a61dc31 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var validator = require('validator'); var meta = require('../meta'); var plugins = require('../plugins'); @@ -46,7 +47,7 @@ searchController.search = function (req, res, next) { }; async.parallel({ - categories: async.apply(categories.buildForSelect, req.uid), + categories: async.apply(categories.buildForSelect, req.uid, 'read'), search: async.apply(search.search, data), }, function (err, results) { if (err) { @@ -68,7 +69,8 @@ searchController.search = function (req, res, next) { searchData.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[global:search]]' }]); searchData.expandSearch = !req.query.term; searchData.searchDefaultSortBy = meta.config.searchDefaultSortBy || ''; - + searchData.search_query = validator.escape(String(req.query.term || '')); + searchData.term = req.query.term; res.render('search', searchData); }); }; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index eb69d48222..d560fbcb2a 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -15,7 +15,7 @@ var helpers = require('./helpers'); var pagination = require('../pagination'); var utils = require('../utils'); -var topicsController = {}; +var topicsController = module.exports; topicsController.get = function (req, res, callback) { var tid = req.params.topic_id; @@ -23,6 +23,7 @@ topicsController.get = function (req, res, callback) { var pageCount = 1; var userPrivileges; var settings; + var rssToken; if ((req.params.post_index && !utils.isNumber(req.params.post_index)) || !utils.isNumber(tid)) { return callback(); @@ -40,6 +41,9 @@ topicsController.get = function (req, res, callback) { topic: function (next) { topics.getTopicData(tid, next); }, + rssToken: function (next) { + user.auth.getFeedToken(req.uid, next); + }, }, next); }, function (results, next) { @@ -48,6 +52,7 @@ topicsController.get = function (req, res, callback) { } userPrivileges = results.privileges; + rssToken = results.rssToken; if (!userPrivileges['topics:read'] || (parseInt(results.topic.deleted, 10) && !userPrivileges.view_deleted)) { return helpers.notAllowed(req, res); @@ -129,166 +134,174 @@ topicsController.get = function (req, res, callback) { plugins.fireHook('filter:controllers.topic.get', { topicData: topicData, uid: req.uid }, next); }, function (data, next) { - var breadcrumbs = [ - { - text: data.topicData.category.name, - url: nconf.get('relative_path') + '/category/' + data.topicData.category.slug, - }, - { - text: data.topicData.title, - }, - ]; - - helpers.buildCategoryBreadcrumbs(data.topicData.category.parentCid, function (err, crumbs) { - if (err) { - return next(err); - } - data.topicData.breadcrumbs = crumbs.concat(breadcrumbs); - next(null, data.topicData); - }); + buildBreadcrumbs(data.topicData, next); }, - function (topicData, next) { - function findPost(index) { - for (var i = 0; i < topicData.posts.length; i += 1) { - if (parseInt(topicData.posts[i].index, 10) === parseInt(index, 10)) { - return topicData.posts[i]; + function (topicData) { + topicData.privileges = userPrivileges; + topicData.topicStaleDays = parseInt(meta.config.topicStaleDays, 10) || 60; + topicData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1; + topicData['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1; + topicData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; + topicData.bookmarkThreshold = parseInt(meta.config.bookmarkThreshold, 10) || 5; + topicData.postEditDuration = parseInt(meta.config.postEditDuration, 10) || 0; + topicData.postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10) || 0; + topicData.scrollToMyPost = settings.scrollToMyPost; + topicData.rssFeedUrl = nconf.get('relative_path') + '/topic/' + topicData.tid + '.rss'; + if (req.uid) { + topicData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken; + } + + addTags(topicData, req, res); + + topicData.postIndex = req.params.post_index; + topicData.pagination = pagination.create(currentPage, pageCount, req.query); + topicData.pagination.rel.forEach(function (rel) { + rel.href = nconf.get('url') + '/topic/' + topicData.slug + rel.href; + res.locals.linkTags.push(rel); + }); + + req.session.tids_viewed = req.session.tids_viewed || {}; + if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < Date.now() - 3600000) { + topics.increaseViewCount(tid); + req.session.tids_viewed[tid] = Date.now(); + } + + if (req.uid) { + topics.markAsRead([tid], req.uid, function (err, markedRead) { + if (err) { + return callback(err); + } + if (markedRead) { + topics.pushUnreadCount(req.uid); + topics.markTopicNotificationsRead([tid], req.uid); } - } - } - var description = ''; - var postAtIndex = findPost(Math.max(0, req.params.post_index - 1)); - - if (postAtIndex && postAtIndex.content) { - description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s; - } - - if (description.length > 255) { - description = description.substr(0, 255) + '...'; - } - - var ogImageUrl = ''; - if (topicData.thumb) { - ogImageUrl = topicData.thumb; - } else if (postAtIndex && postAtIndex.user && postAtIndex.user.picture) { - ogImageUrl = postAtIndex.user.picture; - } else if (meta.config['og:image']) { - ogImageUrl = meta.config['og:image']; - } else if (meta.config['brand:logo']) { - ogImageUrl = meta.config['brand:logo']; - } else { - ogImageUrl = '/logo.png'; - } - - if (typeof ogImageUrl === 'string' && ogImageUrl.indexOf('http') === -1) { - ogImageUrl = nconf.get('url') + ogImageUrl; - } - - description = description.replace(/\n/g, ' '); - - res.locals.metaTags = [ - { - name: 'title', - content: topicData.titleRaw, - }, - { - name: 'description', - content: description, - }, - { - property: 'og:title', - content: topicData.titleRaw, - }, - { - property: 'og:description', - content: description, - }, - { - property: 'og:type', - content: 'article', - }, - { - property: 'og:image', - content: ogImageUrl, - noEscape: true, - }, - { - property: 'og:image:url', - content: ogImageUrl, - noEscape: true, - }, - { - property: 'article:published_time', - content: utils.toISOString(topicData.timestamp), - }, - { - property: 'article:modified_time', - content: utils.toISOString(topicData.lastposttime), - }, - { - property: 'article:section', - content: topicData.category ? topicData.category.name : '', - }, - ]; - - res.locals.linkTags = [ - { - rel: 'alternate', - type: 'application/rss+xml', - href: nconf.get('url') + '/topic/' + tid + '.rss', - }, - ]; - - if (topicData.category) { - res.locals.linkTags.push({ - rel: 'up', - href: nconf.get('url') + '/category/' + topicData.category.slug, }); } + res.render('topic', topicData); + }, + ], callback); +}; + +function buildBreadcrumbs(topicData, callback) { + var breadcrumbs = [ + { + text: topicData.category.name, + url: nconf.get('relative_path') + '/category/' + topicData.category.slug, + }, + { + text: topicData.title, + }, + ]; + + async.waterfall([ + function (next) { + helpers.buildCategoryBreadcrumbs(topicData.category.parentCid, next); + }, + function (crumbs, next) { + topicData.breadcrumbs = crumbs.concat(breadcrumbs); next(null, topicData); }, - ], function (err, data) { - if (err) { - return callback(err); - } + ], callback); +} - data.privileges = userPrivileges; - data.topicStaleDays = parseInt(meta.config.topicStaleDays, 10) || 60; - data['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1; - data['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1; - data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; - data.bookmarkThreshold = parseInt(meta.config.bookmarkThreshold, 10) || 5; - data.postEditDuration = parseInt(meta.config.postEditDuration, 10) || 0; - data.postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10) || 0; - data.scrollToMyPost = settings.scrollToMyPost; - data.rssFeedUrl = nconf.get('relative_path') + '/topic/' + data.tid + '.rss'; - data.pagination = pagination.create(currentPage, pageCount, req.query); - data.pagination.rel.forEach(function (rel) { - rel.href = nconf.get('url') + '/topic/' + data.slug + rel.href; - res.locals.linkTags.push(rel); +function addTags(topicData, req, res) { + function findPost(index) { + for (var i = 0; i < topicData.posts.length; i += 1) { + if (parseInt(topicData.posts[i].index, 10) === parseInt(index, 10)) { + return topicData.posts[i]; + } + } + } + var description = ''; + var postAtIndex = findPost(Math.max(0, req.params.post_index - 1)); + + if (postAtIndex && postAtIndex.content) { + description = S(postAtIndex.content).decodeHTMLEntities().stripTags().s; + } + + if (description.length > 255) { + description = description.substr(0, 255) + '...'; + } + + var ogImageUrl = ''; + if (topicData.thumb) { + ogImageUrl = topicData.thumb; + } else if (postAtIndex && postAtIndex.user && postAtIndex.user.picture) { + ogImageUrl = postAtIndex.user.picture; + } else if (meta.config['og:image']) { + ogImageUrl = meta.config['og:image']; + } else if (meta.config['brand:logo']) { + ogImageUrl = meta.config['brand:logo']; + } else { + ogImageUrl = '/logo.png'; + } + + if (typeof ogImageUrl === 'string' && ogImageUrl.indexOf('http') === -1) { + ogImageUrl = nconf.get('url') + ogImageUrl; + } + + description = description.replace(/\n/g, ' '); + res.locals.metaTags = [ + { + name: 'title', + content: topicData.titleRaw, + }, + { + name: 'description', + content: description, + }, + { + property: 'og:title', + content: topicData.titleRaw, + }, + { + property: 'og:description', + content: description, + }, + { + property: 'og:type', + content: 'article', + }, + { + property: 'og:image', + content: ogImageUrl, + noEscape: true, + }, + { + property: 'og:image:url', + content: ogImageUrl, + noEscape: true, + }, + { + property: 'article:published_time', + content: utils.toISOString(topicData.timestamp), + }, + { + property: 'article:modified_time', + content: utils.toISOString(topicData.lastposttime), + }, + { + property: 'article:section', + content: topicData.category ? topicData.category.name : '', + }, + ]; + + res.locals.linkTags = [ + { + rel: 'alternate', + type: 'application/rss+xml', + href: topicData.rssFeedUrl, + }, + ]; + + if (topicData.category) { + res.locals.linkTags.push({ + rel: 'up', + href: nconf.get('url') + '/category/' + topicData.category.slug, }); - - req.session.tids_viewed = req.session.tids_viewed || {}; - if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < Date.now() - 3600000) { - topics.increaseViewCount(tid); - req.session.tids_viewed[tid] = Date.now(); - } - - if (req.uid) { - topics.markAsRead([tid], req.uid, function (err, markedRead) { - if (err) { - return callback(err); - } - if (markedRead) { - topics.pushUnreadCount(req.uid); - topics.markTopicNotificationsRead([tid], req.uid); - } - }); - } - - res.render('topic', data); - }); -}; + } +} topicsController.teaser = function (req, res, next) { var tid = req.params.topic_id; @@ -354,5 +367,3 @@ topicsController.pagination = function (req, res, callback) { res.json(paginationData); }); }; - -module.exports = topicsController; diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index cae09a5043..ab651d1325 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -217,13 +217,10 @@ uploadsController.uploadFile = function (uid, uploadedFile, callback) { }; function saveFileToLocal(uploadedFile, callback) { - var extension = path.extname(uploadedFile.name); - if (!extension || extension === '.') { - return callback(new Error('[[error:invalid-extension]]')); - } var filename = uploadedFile.name || 'upload'; + var extension = path.extname(filename) || ''; - filename = Date.now() + '-' + validator.escape(filename.replace(path.extname(uploadedFile.name) || '', '')).substr(0, 255) + extension; + filename = Date.now() + '-' + validator.escape(filename.substr(0, filename.length - extension.length)).substr(0, 255) + extension; async.waterfall([ function (next) { diff --git a/src/database/mongo.js b/src/database/mongo.js index 14dcd330ac..c79ee81bdc 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -206,18 +206,18 @@ mongoModule.info = function (db, callback) { stats.mem = results.serverStatus.mem; stats.mem = results.serverStatus.mem; - stats.mem.resident = (stats.mem.resident / 1024).toFixed(2); - stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(2); - stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(2); + stats.mem.resident = (stats.mem.resident / 1024).toFixed(3); + stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(3); + stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(3); stats.collectionData = results.listCollections; stats.network = results.serverStatus.network; stats.raw = JSON.stringify(stats, null, 4); stats.avgObjSize = stats.avgObjSize.toFixed(2); - stats.dataSize = (stats.dataSize / scale).toFixed(2); - stats.storageSize = (stats.storageSize / scale).toFixed(2); - stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0; - stats.indexSize = (stats.indexSize / scale).toFixed(2); + stats.dataSize = (stats.dataSize / scale).toFixed(3); + stats.storageSize = (stats.storageSize / scale).toFixed(3); + stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(3) : 0; + stats.indexSize = (stats.indexSize / scale).toFixed(3); stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1'; stats.host = results.serverStatus.host; stats.version = results.serverStatus.version; diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 4168199bb5..feeed1e20a 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -467,38 +467,46 @@ module.exports = function (db, module) { } } - module.processSortedSet = function (setKey, process, batch, callback) { + module.processSortedSet = function (setKey, process, options, callback) { var done = false; var ids = []; var cursor = db.collection('objects').find({ _key: setKey }) .sort({ score: 1 }) .project({ _id: 0, value: 1 }) - .batchSize(batch); + .batchSize(options.batch); async.whilst( function () { return !done; }, function (next) { - cursor.next(function (err, item) { - if (err) { - return next(err); - } - if (item === null) { - done = true; - } else { - ids.push(item.value); - } + async.waterfall([ + function (next) { + cursor.next(next); + }, + function (item, _next) { + if (item === null) { + done = true; + } else { + ids.push(item.value); + } - if (ids.length < batch && (!done || ids.length === 0)) { - return next(null); - } - - process(ids, function (err) { + if (ids.length < options.batch && (!done || ids.length === 0)) { + return next(null); + } + process(ids, function (err) { + _next(err); + }); + }, + function (next) { ids = []; - return next(err); - }); - }); + if (options.interval) { + setTimeout(next, options.interval); + } else { + next(); + } + }, + ], next); }, callback ); diff --git a/src/database/redis.js b/src/database/redis.js index 75129b6c8b..13d87c27bd 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -152,7 +152,7 @@ redisModule.info = function (cxn, callback) { redisData[parts[0]] = parts[1]; } }); - redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(2); + redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(3); redisData.raw = JSON.stringify(redisData, null, 4); redisData.redis = true; diff --git a/src/events.js b/src/events.js index 48307b868d..ffce83beb4 100644 --- a/src/events.js +++ b/src/events.js @@ -138,3 +138,19 @@ events.deleteAll = function (callback) { events.deleteEvents(eids, next); }, { alwaysStartAt: 0 }, callback); }; + +events.output = function () { + process.stdout.write('\nDisplaying last ten administrative events...\n'.bold); + events.getEvents(0, 9, function (err, events) { + if (err) { + process.stdout.write(' Error '.red + String(err.message).reset); + process.exit(1); + } + + events.forEach(function (event) { + process.stdout.write(' * ' + String(event.timestampISO).green + ' ' + String(event.type).yellow + (event.text ? ' ' + event.text : '') + ' (uid: '.reset + (event.uid ? event.uid : 0) + ')\n'); + }); + + process.exit(0); + }); +}; diff --git a/src/flags.js b/src/flags.js index 1f6d932de1..9e3059d2a4 100644 --- a/src/flags.js +++ b/src/flags.js @@ -3,6 +3,7 @@ var async = require('async'); var _ = require('lodash'); var S = require('string'); +var winston = require('winston'); var db = require('./database'); var user = require('./user'); @@ -18,6 +19,60 @@ var utils = require('../public/src/utils'); var Flags = module.exports; +Flags.init = function (callback) { + // Query plugins for custom filter strategies and merge into core filter strategies + var prepareSets = function (sets, orSets, prefix, value) { + if (!Array.isArray(value)) { + sets.push(prefix + value); + } else if (value.length) { + value.forEach(function (x) { + orSets.push(prefix + x); + }); + } + }; + + plugins.fireHook('filter:flags.getFilters', { + filters: { + type: function (sets, orSets, key) { + prepareSets(sets, orSets, 'flags:byType:', key); + }, + state: function (sets, orSets, key) { + prepareSets(sets, orSets, 'flags:byState:', key); + }, + reporterId: function (sets, orSets, key) { + prepareSets(sets, orSets, 'flags:byReporter:', key); + }, + assignee: function (sets, orSets, key) { + prepareSets(sets, orSets, 'flags:byAssignee:', key); + }, + targetUid: function (sets, orSets, key) { + prepareSets(sets, orSets, 'flags:byTargetUid:', key); + }, + cid: function (sets, orSets, key) { + prepareSets(sets, orSets, 'flags:byCid:', key); + }, + quick: function (sets, orSets, key, uid) { + switch (key) { + case 'mine': + sets.push('flags:byAssignee:' + uid); + break; + } + }, + }, + helpers: { + prepareSets: prepareSets, + }, + }, function (err, data) { + if (err) { + winston.error('[flags/init] Could not retrieve filters (error: ' + err.message + ')'); + data.filters = {}; + } + + Flags._filters = data.filters; + callback(); + }); +}; + Flags.get = function (flagId, callback) { async.waterfall([ // First stage @@ -46,6 +101,13 @@ Flags.get = function (flagId, callback) { })); }); }, + function (flagObj, next) { + plugins.fireHook('filter:flags.get', { + flag: flagObj, + }, function (err, data) { + next(err, data.flag); + }); + }, ], callback); }; @@ -57,51 +119,14 @@ Flags.list = function (filters, uid, callback) { var sets = []; var orSets = []; - var prepareSets = function (setPrefix, value) { - if (!Array.isArray(value)) { - sets.push(setPrefix + value); - } else if (value.length) { - value.forEach(function (x) { - orSets.push(setPrefix + x); - }); - } - }; if (Object.keys(filters).length > 0) { for (var type in filters) { if (filters.hasOwnProperty(type)) { - switch (type) { - case 'type': - prepareSets('flags:byType:', filters[type]); - break; - - case 'state': - prepareSets('flags:byState:', filters[type]); - break; - - case 'reporterId': - prepareSets('flags:byReporter:', filters[type]); - break; - - case 'assignee': - prepareSets('flags:byAssignee:', filters[type]); - break; - - case 'targetUid': - prepareSets('flags:byTargetUid:', filters[type]); - break; - - case 'cid': - prepareSets('flags:byCid:', filters[type]); - break; - - case 'quick': - switch (filters.quick) { - case 'mine': - sets.push('flags:byAssignee:' + uid); - break; - } - break; + if (Flags._filters.hasOwnProperty(type)) { + Flags._filters[type](sets, orSets, filters[type], uid); + } else { + winston.warn('[flags/list] No flag filter type found: ' + type); } } } @@ -181,6 +206,13 @@ Flags.list = function (filters, uid, callback) { }); }, next); }, + function (flags, next) { + plugins.fireHook('filter:flags.list', { + flags: flags, + }, function (err, data) { + next(err, data.flags); + }); + }, ], callback); }; @@ -505,7 +537,8 @@ Flags.update = function (flagId, uid, changeset, callback) { tasks.push(async.apply(Flags.appendHistory, flagId, uid, changeset)); // Fire plugin hook - tasks.push(async.apply(plugins.fireHook, 'action:flag.update', { flagId: flagId, changeset: changeset, uid: uid })); + tasks.push(async.apply(plugins.fireHook, 'action:flag.update', { flagId: flagId, changeset: changeset, uid: uid })); // delete @ NodeBB v1.6.0 + tasks.push(async.apply(plugins.fireHook, 'action:flags.update', { flagId: flagId, changeset: changeset, uid: uid })); async.parallel(tasks, function (err) { return next(err); @@ -644,6 +677,9 @@ Flags.notify = function (flagObj, uid, callback) { plugins.fireHook('action:flag.create', { flag: flagObj, + }); // delete @ NodeBB v1.6.0 + plugins.fireHook('action:flags.create', { + flag: flagObj, }); notifications.push(notification, results.admins.concat(results.moderators).concat(results.globalMods), callback); }); @@ -673,6 +709,9 @@ Flags.notify = function (flagObj, uid, callback) { plugins.fireHook('action:flag.create', { flag: flagObj, + }); // delete @ NodeBB v1.6.0 + plugins.fireHook('action:flags.create', { + flag: flagObj, }); notifications.push(notification, results.admins.concat(results.globalMods), callback); }); diff --git a/src/groups/delete.js b/src/groups/delete.js index 4d5fa6b4c0..b0203aa60e 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -20,26 +20,31 @@ module.exports = function (Groups) { groupObj = groupsData[0]; async.parallel([ - async.apply(db.delete, 'group:' + groupName), - async.apply(db.sortedSetRemove, 'groups:createtime', groupName), - async.apply(db.sortedSetRemove, 'groups:visible:createtime', groupName), - async.apply(db.sortedSetRemove, 'groups:visible:memberCount', groupName), - async.apply(db.sortedSetRemove, 'groups:visible:name', groupName.toLowerCase() + ':' + groupName), - async.apply(db.delete, 'group:' + groupName + ':members'), - async.apply(db.delete, 'group:' + groupName + ':pending'), - async.apply(db.delete, 'group:' + groupName + ':invited'), - async.apply(db.delete, 'group:' + groupName + ':owners'), - async.apply(db.delete, 'group:' + groupName + ':member:pids'), - async.apply(db.deleteObjectField, 'groupslug:groupname', utils.slugify(groupName)), function (next) { - batch.processSortedSet('groups:createtime', function (groupNames, next) { - var keys = groupNames.map(function (group) { - return 'group:' + group + ':members'; - }); - db.sortedSetsRemove(keys, groupName, next); - }, { - batch: 500, - }, next); + db.deleteAll([ + 'group:' + groupName, + 'group:' + groupName + ':members', + 'group:' + groupName + ':pending', + 'group:' + groupName + ':invited', + 'group:' + groupName + ':owners', + 'group:' + groupName + ':member:pids', + ], next); + }, + function (next) { + db.sortedSetsRemove([ + 'groups:createtime', + 'groups:visible:createtime', + 'groups:visible:memberCount', + ], groupName, next); + }, + function (next) { + db.sortedSetRemove('groups:visible:name', groupName.toLowerCase() + ':' + groupName, next); + }, + function (next) { + db.deleteObjectField('groupslug:groupname', utils.slugify(groupName), next); + }, + function (next) { + removeGroupFromOtherGroups(groupName, next); }, ], function (err) { next(err); @@ -52,4 +57,15 @@ module.exports = function (Groups) { }, ], callback); }; + + function removeGroupFromOtherGroups(groupName, callback) { + batch.processSortedSet('groups:createtime', function (groupNames, next) { + var keys = groupNames.map(function (group) { + return 'group:' + group + ':members'; + }); + db.sortedSetsRemove(keys, groupName, next); + }, { + batch: 500, + }, callback); + } }; diff --git a/src/meta.js b/src/meta.js index 59c3447cf4..cb2a381d6d 100644 --- a/src/meta.js +++ b/src/meta.js @@ -12,16 +12,16 @@ var Meta = module.exports; Meta.reloadRequired = false; -require('./meta/configs')(Meta); -require('./meta/themes')(Meta); -require('./meta/js')(Meta); -require('./meta/css')(Meta); -require('./meta/sounds')(Meta); -require('./meta/settings')(Meta); -require('./meta/logs')(Meta); -require('./meta/errors')(Meta); -require('./meta/tags')(Meta); -require('./meta/dependencies')(Meta); +Meta.configs = require('./meta/configs'); +Meta.themes = require('./meta/themes'); +Meta.js = require('./meta/js'); +Meta.css = require('./meta/css'); +Meta.sounds = require('./meta/sounds'); +Meta.settings = require('./meta/settings'); +Meta.logs = require('./meta/logs'); +Meta.errors = require('./meta/errors'); +Meta.tags = require('./meta/tags'); +Meta.dependencies = require('./meta/dependencies'); Meta.templates = require('./meta/templates'); Meta.blacklist = require('./meta/blacklist'); Meta.languages = require('./meta/languages'); diff --git a/src/meta/build.js b/src/meta/build.js index 29155d55c3..26bec8b5e8 100644 --- a/src/meta/build.js +++ b/src/meta/build.js @@ -3,7 +3,7 @@ var async = require('async'); var winston = require('winston'); var nconf = require('nconf'); -var padstart = require('lodash.padstart'); +var _ = require('lodash'); var cacheBuster = require('./cacheBuster'); var meta; @@ -122,7 +122,7 @@ function buildTargets(targets, parallel, callback) { })); all(targets, function (target, next) { - targetHandlers[target](parallel, step(padstart(target, length) + ' ', next)); + targetHandlers[target](parallel, step(_.padStart(target, length) + ' ', next)); }, callback); } diff --git a/src/meta/configs.js b/src/meta/configs.js index daaa807d19..925ff61255 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -6,142 +6,142 @@ var nconf = require('nconf'); var db = require('../database'); var pubsub = require('../pubsub'); +var Meta = require('../meta'); var cacheBuster = require('./cacheBuster'); -module.exports = function (Meta) { - Meta.config = {}; - Meta.configs = {}; +var Configs = module.exports; - Meta.configs.init = function (callback) { - delete Meta.config; +Meta.config = {}; - async.waterfall([ - function (next) { - Meta.configs.list(next); - }, - function (config, next) { - cacheBuster.read(function (err, buster) { - if (err) { - return next(err); - } +Configs.init = function (callback) { + Meta.config = null; - config['cache-buster'] = 'v=' + (buster || Date.now()); - - Meta.config = config; - next(); - }); - }, - ], callback); - }; - - Meta.configs.list = function (callback) { - db.getObject('config', function (err, config) { - config = config || {}; - config.version = nconf.get('version'); - config.registry = nconf.get('registry'); - callback(err, config); - }); - }; - - Meta.configs.get = function (field, callback) { - db.getObjectField('config', field, callback); - }; - - Meta.configs.getFields = function (fields, callback) { - db.getObjectFields('config', fields, callback); - }; - - Meta.configs.set = function (field, value, callback) { - callback = callback || function () {}; - if (!field) { - return callback(new Error('[[error:invalid-data]]')); - } - - var data = {}; - data[field] = value; - Meta.configs.setMultiple(data, callback); - }; - - - Meta.configs.setMultiple = function (data, callback) { - async.waterfall([ - function (next) { - processConfig(data, next); - }, - function (next) { - db.setObject('config', data, next); - }, - function (next) { - updateConfig(data); - setImmediate(next); - }, - ], callback); - }; - - function processConfig(data, callback) { - if (data.customCSS) { - return saveRenderedCss(data, callback); - } - setImmediate(callback); - } - - function saveRenderedCss(data, callback) { - var less = require('less'); - async.waterfall([ - function (next) { - less.render(data.customCSS, { - compress: true, - }, next); - }, - function (lessObject, next) { - data.renderedCustomCSS = lessObject.css; - setImmediate(next); - }, - ], callback); - } - - function updateConfig(config) { - updateLocalConfig(config); - pubsub.publish('config:update', config); - } - - function updateLocalConfig(config) { - for (var field in config) { - if (config.hasOwnProperty(field)) { - Meta.config[field] = config[field]; - } - } - } - - pubsub.on('config:update', function onConfigReceived(config) { - if (typeof config === 'object' && Meta.config) { - updateLocalConfig(config); - } - }); - - Meta.configs.setOnEmpty = function (values, callback) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function (data, next) { - data = data || {}; - var empty = {}; - Object.keys(values).forEach(function (key) { - if (!data.hasOwnProperty(key)) { - empty[key] = values[key]; - } - }); - if (Object.keys(empty).length) { - db.setObject('config', empty, next); - } else { - setImmediate(next); + async.waterfall([ + function (next) { + Configs.list(next); + }, + function (config, next) { + cacheBuster.read(function (err, buster) { + if (err) { + return next(err); } - }, - ], callback); - }; - Meta.configs.remove = function (field, callback) { - db.deleteObjectField('config', field, callback); - }; + config['cache-buster'] = 'v=' + (buster || Date.now()); + + Meta.config = config; + next(); + }); + }, + ], callback); +}; + +Configs.list = function (callback) { + db.getObject('config', function (err, config) { + config = config || {}; + config.version = nconf.get('version'); + config.registry = nconf.get('registry'); + callback(err, config); + }); +}; + +Configs.get = function (field, callback) { + db.getObjectField('config', field, callback); +}; + +Configs.getFields = function (fields, callback) { + db.getObjectFields('config', fields, callback); +}; + +Configs.set = function (field, value, callback) { + callback = callback || function () {}; + if (!field) { + return callback(new Error('[[error:invalid-data]]')); + } + + var data = {}; + data[field] = value; + Configs.setMultiple(data, callback); +}; + + +Configs.setMultiple = function (data, callback) { + async.waterfall([ + function (next) { + processConfig(data, next); + }, + function (next) { + db.setObject('config', data, next); + }, + function (next) { + updateConfig(data); + setImmediate(next); + }, + ], callback); +}; + +function processConfig(data, callback) { + if (data.customCSS) { + return saveRenderedCss(data, callback); + } + setImmediate(callback); +} + +function saveRenderedCss(data, callback) { + var less = require('less'); + async.waterfall([ + function (next) { + less.render(data.customCSS, { + compress: true, + }, next); + }, + function (lessObject, next) { + data.renderedCustomCSS = lessObject.css; + setImmediate(next); + }, + ], callback); +} + +function updateConfig(config) { + updateLocalConfig(config); + pubsub.publish('config:update', config); +} + +function updateLocalConfig(config) { + for (var field in config) { + if (config.hasOwnProperty(field)) { + Meta.config[field] = config[field]; + } + } +} + +pubsub.on('config:update', function onConfigReceived(config) { + if (typeof config === 'object' && Meta.config) { + updateLocalConfig(config); + } +}); + +Configs.setOnEmpty = function (values, callback) { + async.waterfall([ + function (next) { + db.getObject('config', next); + }, + function (data, next) { + data = data || {}; + var empty = {}; + Object.keys(values).forEach(function (key) { + if (!data.hasOwnProperty(key)) { + empty[key] = values[key]; + } + }); + if (Object.keys(empty).length) { + db.setObject('config', empty, next); + } else { + setImmediate(next); + } + }, + ], callback); +}; + +Configs.remove = function (field, callback) { + db.deleteObjectField('config', field, callback); }; diff --git a/src/meta/css.js b/src/meta/css.js index f2abe608ac..0f20ed7aca 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -11,158 +11,150 @@ var db = require('../database'); var file = require('../file'); var minifier = require('./minifier'); -module.exports = function (Meta) { - Meta.css = {}; +var CSS = module.exports; - var buildImports = { - client: function (source) { - return '@import "./theme";\n' + source + '\n' + [ - '@import "font-awesome";', - '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', - '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', - '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', - '@import (inline) "../node_modules/cropperjs/dist/cropper.css";', - '@import "../../public/less/flags.less";', - '@import "../../public/less/blacklist.less";', - '@import "../../public/less/generics.less";', - '@import "../../public/less/mixins.less";', - '@import "../../public/less/global.less";', - ].map(function (str) { - return str.replace(/\//g, path.sep); - }).join('\n'); - }, - admin: function (source) { - return source + '\n' + [ - '@import "font-awesome";', - '@import "../public/less/admin/admin";', - '@import "../public/less/generics.less";', - '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', - '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', - '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', - '@import (inline) "../public/vendor/mdl/material.css";', - ].map(function (str) { - return str.replace(/\//g, path.sep); - }).join('\n'); - }, - }; - - function filterMissingFiles(filepaths, callback) { - async.filter(filepaths, function (filepath, next) { - file.exists(path.join(__dirname, '../../node_modules', filepath), function (err, exists) { - if (!exists) { - winston.warn('[meta/css] File not found! ' + filepath); - } - - next(err, exists); - }); - }, callback); - } - - function getImports(files, prefix, extension, callback) { - var pluginDirectories = []; - var source = ''; - - files.forEach(function (styleFile) { - if (styleFile.endsWith(extension)) { - source += prefix + path.sep + styleFile + '";'; - } else { - pluginDirectories.push(styleFile); - } - }); - - async.each(pluginDirectories, function (directory, next) { - file.walk(directory, function (err, styleFiles) { - if (err) { - return next(err); - } - - styleFiles.forEach(function (styleFile) { - source += prefix + path.sep + styleFile + '";'; - }); - - next(); - }); - }, function (err) { - callback(err, source); - }); - } - - function getBundleMetadata(target, callback) { - var paths = [ - path.join(__dirname, '../../node_modules'), - path.join(__dirname, '../../public/vendor/fontawesome/less'), - ]; - - async.waterfall([ - function (next) { - if (target !== 'client') { - return next(null, null); - } - - db.getObjectFields('config', ['theme:type', 'theme:id'], next); - }, - function (themeData, next) { - if (target === 'client') { - var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'); - var baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')); - paths.unshift(baseThemePath); - } - - async.parallel({ - less: function (cb) { - async.waterfall([ - function (next) { - filterMissingFiles(plugins.lessFiles, next); - }, - function (lessFiles, next) { - getImports(lessFiles, '\n@import ".', '.less', next); - }, - ], cb); - }, - css: function (cb) { - async.waterfall([ - function (next) { - filterMissingFiles(plugins.cssFiles, next); - }, - function (cssFiles, next) { - getImports(cssFiles, '\n@import (inline) ".', '.css', next); - }, - ], cb); - }, - }, next); - }, - function (result, next) { - var cssImports = result.css; - var lessImports = result.less; - - var imports = cssImports + '\n' + lessImports; - imports = buildImports[target](imports); - - next(null, imports); - }, - ], function (err, imports) { - if (err) { - return callback(err); - } - - callback(null, { paths: paths, imports: imports }); - }); - } - - Meta.css.buildBundle = function (target, fork, callback) { - async.waterfall([ - function (next) { - getBundleMetadata(target, next); - }, - function (data, next) { - var minify = global.env !== 'development'; - minifier.css.bundle(data.imports, data.paths, minify, fork, next); - }, - function (bundle, next) { - var filename = (target === 'client' ? 'stylesheet' : 'admin') + '.css'; - - fs.writeFile(path.join(__dirname, '../../build/public', filename), bundle.code, next); - }, - ], callback); - }; +var buildImports = { + client: function (source) { + return '@import "./theme";\n' + source + '\n' + [ + '@import "font-awesome";', + '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', + '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', + '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', + '@import (inline) "../node_modules/cropperjs/dist/cropper.css";', + '@import "../../public/less/flags.less";', + '@import "../../public/less/blacklist.less";', + '@import "../../public/less/generics.less";', + '@import "../../public/less/mixins.less";', + '@import "../../public/less/global.less";', + ].map(function (str) { + return str.replace(/\//g, path.sep); + }).join('\n'); + }, + admin: function (source) { + return source + '\n' + [ + '@import "font-awesome";', + '@import "../public/less/admin/admin";', + '@import "../public/less/generics.less";', + '@import (inline) "../public/vendor/colorpicker/colorpicker.css";', + '@import (inline) "../public/vendor/jquery/css/smoothness/jquery-ui.css";', + '@import (inline) "../public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";', + '@import (inline) "../public/vendor/mdl/material.css";', + ].map(function (str) { + return str.replace(/\//g, path.sep); + }).join('\n'); + }, +}; + +function filterMissingFiles(filepaths, callback) { + async.filter(filepaths, function (filepath, next) { + file.exists(path.join(__dirname, '../../node_modules', filepath), function (err, exists) { + if (!exists) { + winston.warn('[meta/css] File not found! ' + filepath); + } + + next(err, exists); + }); + }, callback); +} + +function getImports(files, prefix, extension, callback) { + var pluginDirectories = []; + var source = ''; + + files.forEach(function (styleFile) { + if (styleFile.endsWith(extension)) { + source += prefix + path.sep + styleFile + '";'; + } else { + pluginDirectories.push(styleFile); + } + }); + + async.each(pluginDirectories, function (directory, next) { + file.walk(directory, function (err, styleFiles) { + if (err) { + return next(err); + } + + styleFiles.forEach(function (styleFile) { + source += prefix + path.sep + styleFile + '";'; + }); + + next(); + }); + }, function (err) { + callback(err, source); + }); +} + +function getBundleMetadata(target, callback) { + var paths = [ + path.join(__dirname, '../../node_modules'), + path.join(__dirname, '../../public/vendor/fontawesome/less'), + ]; + + async.waterfall([ + function (next) { + if (target !== 'client') { + return next(null, null); + } + + db.getObjectFields('config', ['theme:type', 'theme:id'], next); + }, + function (themeData, next) { + if (target === 'client') { + var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'); + var baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')); + paths.unshift(baseThemePath); + } + + async.parallel({ + less: function (cb) { + async.waterfall([ + function (next) { + filterMissingFiles(plugins.lessFiles, next); + }, + function (lessFiles, next) { + getImports(lessFiles, '\n@import ".', '.less', next); + }, + ], cb); + }, + css: function (cb) { + async.waterfall([ + function (next) { + filterMissingFiles(plugins.cssFiles, next); + }, + function (cssFiles, next) { + getImports(cssFiles, '\n@import (inline) ".', '.css', next); + }, + ], cb); + }, + }, next); + }, + function (result, next) { + var cssImports = result.css; + var lessImports = result.less; + + var imports = cssImports + '\n' + lessImports; + imports = buildImports[target](imports); + + next(null, { paths: paths, imports: imports }); + }, + ], callback); +} + +CSS.buildBundle = function (target, fork, callback) { + async.waterfall([ + function (next) { + getBundleMetadata(target, next); + }, + function (data, next) { + var minify = global.env !== 'development'; + minifier.css.bundle(data.imports, data.paths, minify, fork, next); + }, + function (bundle, next) { + var filename = (target === 'client' ? 'stylesheet' : 'admin') + '.css'; + + fs.writeFile(path.join(__dirname, '../../build/public', filename), bundle.code, next); + }, + ], callback); }; diff --git a/src/meta/debugParams.js b/src/meta/debugParams.js new file mode 100644 index 0000000000..952dc8dd04 --- /dev/null +++ b/src/meta/debugParams.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = function () { + var debugArg = process.execArgv.find(function (arg) { + return /^--(debug|inspect)/.test(arg); + }); + if (global.v8debug || debugArg) { + debugArg = debugArg ? debugArg.split('=') : ['--debug', 5859]; + var num = parseInt(debugArg[1], 10) + 1; + + return { execArgv: [debugArg[0] + '=' + num, '--nolazy'] }; + } + + return { execArgv: [] }; +}; diff --git a/src/meta/dependencies.js b/src/meta/dependencies.js index 3a16f0e9e5..db403732ec 100644 --- a/src/meta/dependencies.js +++ b/src/meta/dependencies.js @@ -9,73 +9,72 @@ require('colors'); var pkg = require('../../package.json'); -module.exports = function (Meta) { - Meta.dependencies = {}; - var depsMissing = false; - var depsOutdated = false; +var Dependencies = module.exports; - Meta.dependencies.check = function (callback) { - var modules = Object.keys(pkg.dependencies); +var depsMissing = false; +var depsOutdated = false; - winston.verbose('Checking dependencies for outdated modules'); +Dependencies.check = function (callback) { + var modules = Object.keys(pkg.dependencies); - async.each(modules, Meta.dependencies.checkModule, function (err) { - if (err) { - return callback(err); - } + winston.verbose('Checking dependencies for outdated modules'); - if (depsMissing) { - callback(new Error('dependencies-missing')); - } else if (depsOutdated) { - callback(global.env !== 'development' ? new Error('dependencies-out-of-date') : null); - } else { - callback(null); - } - }); - }; - - Meta.dependencies.checkModule = function (moduleName, callback) { - fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), { - encoding: 'utf-8', - }, function (err, pkgData) { - if (err) { - // If a bundled plugin/theme is not present, skip the dep check (#3384) - if (err.code === 'ENOENT' && (moduleName === 'nodebb-rewards-essentials' || moduleName.startsWith('nodebb-plugin') || moduleName.startsWith('nodebb-theme'))) { - winston.warn('[meta/dependencies] Bundled plugin ' + moduleName + ' not found, skipping dependency check.'); - return callback(null, true); - } - return callback(err); - } - - pkgData = Meta.dependencies.parseModuleData(moduleName, pkgData); - - var satisfies = Meta.dependencies.doesSatisfy(pkgData, pkg.dependencies[moduleName]); - callback(null, satisfies); - }); - }; - - Meta.dependencies.parseModuleData = function (moduleName, pkgData) { - try { - pkgData = JSON.parse(pkgData); - } catch (e) { - winston.warn('[' + 'missing'.red + '] ' + moduleName.bold + ' is a required dependency but could not be found\n'); - depsMissing = true; - return null; + async.each(modules, Dependencies.checkModule, function (err) { + if (err) { + return callback(err); } - return pkgData; - }; - Meta.dependencies.doesSatisfy = function (moduleData, packageJSONVersion) { - if (!moduleData) { - return false; + if (depsMissing) { + callback(new Error('dependencies-missing')); + } else if (depsOutdated) { + callback(global.env !== 'development' ? new Error('dependencies-out-of-date') : null); + } else { + callback(null); } - var versionOk = !semver.validRange(packageJSONVersion) || semver.satisfies(moduleData.version, packageJSONVersion); - var githubRepo = moduleData._resolved && moduleData._resolved.indexOf('//github.com') !== -1; - var satisfies = versionOk || githubRepo; - if (!satisfies) { - winston.warn('[' + 'outdated'.yellow + '] ' + moduleData.name.bold + ' installed v' + moduleData.version + ', package.json requires ' + packageJSONVersion + '\n'); - depsOutdated = true; - } - return satisfies; - }; + }); +}; + +Dependencies.checkModule = function (moduleName, callback) { + fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), { + encoding: 'utf-8', + }, function (err, pkgData) { + if (err) { + // If a bundled plugin/theme is not present, skip the dep check (#3384) + if (err.code === 'ENOENT' && (moduleName === 'nodebb-rewards-essentials' || moduleName.startsWith('nodebb-plugin') || moduleName.startsWith('nodebb-theme'))) { + winston.warn('[meta/dependencies] Bundled plugin ' + moduleName + ' not found, skipping dependency check.'); + return callback(null, true); + } + return callback(err); + } + + pkgData = Dependencies.parseModuleData(moduleName, pkgData); + + var satisfies = Dependencies.doesSatisfy(pkgData, pkg.dependencies[moduleName]); + callback(null, satisfies); + }); +}; + +Dependencies.parseModuleData = function (moduleName, pkgData) { + try { + pkgData = JSON.parse(pkgData); + } catch (e) { + winston.warn('[' + 'missing'.red + '] ' + moduleName.bold + ' is a required dependency but could not be found\n'); + depsMissing = true; + return null; + } + return pkgData; +}; + +Dependencies.doesSatisfy = function (moduleData, packageJSONVersion) { + if (!moduleData) { + return false; + } + var versionOk = !semver.validRange(packageJSONVersion) || semver.satisfies(moduleData.version, packageJSONVersion); + var githubRepo = moduleData._resolved && moduleData._resolved.indexOf('//github.com') !== -1; + var satisfies = versionOk || githubRepo; + if (!satisfies) { + winston.warn('[' + 'outdated'.yellow + '] ' + moduleData.name.bold + ' installed v' + moduleData.version + ', package.json requires ' + packageJSONVersion + '\n'); + depsOutdated = true; + } + return satisfies; }; diff --git a/src/meta/errors.js b/src/meta/errors.js index f269490bbf..38e206e501 100644 --- a/src/meta/errors.js +++ b/src/meta/errors.js @@ -8,61 +8,59 @@ var cronJob = require('cron').CronJob; var db = require('../database'); var analytics = require('../analytics'); -module.exports = function (Meta) { - Meta.errors = {}; +var Errors = module.exports; - var counters = {}; +var counters = {}; - new cronJob('0 * * * * *', function () { - Meta.errors.writeData(); - }, null, true); +new cronJob('0 * * * * *', function () { + Errors.writeData(); +}, null, true); - Meta.errors.writeData = function () { - var dbQueue = []; - if (Object.keys(counters).length > 0) { - for (var key in counters) { - if (counters.hasOwnProperty(key)) { - dbQueue.push(async.apply(db.sortedSetIncrBy, 'errors:404', counters[key], key)); - } +Errors.writeData = function () { + var dbQueue = []; + if (Object.keys(counters).length > 0) { + for (var key in counters) { + if (counters.hasOwnProperty(key)) { + dbQueue.push(async.apply(db.sortedSetIncrBy, 'errors:404', counters[key], key)); } - counters = {}; - async.series(dbQueue, function (err) { - if (err) { - winston.error(err); - } - }); } - }; - - Meta.errors.log404 = function (route, callback) { - callback = callback || function () {}; - if (!route) { - return setImmediate(callback); - } - route = route.replace(/\/$/, ''); // remove trailing slashes - analytics.increment('errors:404'); - counters[route] = counters[route] || 0; - counters[route] += 1; - setImmediate(callback); - }; - - Meta.errors.get = function (escape, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRevRangeWithScores('errors:404', 0, 199, next); - }, - function (data, next) { - data = data.map(function (nfObject) { - nfObject.value = escape ? validator.escape(String(nfObject.value || '')) : nfObject.value; - return nfObject; - }); - - next(null, data); - }, - ], callback); - }; - - Meta.errors.clear = function (callback) { - db.delete('errors:404', callback); - }; + counters = {}; + async.series(dbQueue, function (err) { + if (err) { + winston.error(err); + } + }); + } +}; + +Errors.log404 = function (route, callback) { + callback = callback || function () {}; + if (!route) { + return setImmediate(callback); + } + route = route.replace(/\/$/, ''); // remove trailing slashes + analytics.increment('errors:404'); + counters[route] = counters[route] || 0; + counters[route] += 1; + setImmediate(callback); +}; + +Errors.get = function (escape, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRangeWithScores('errors:404', 0, 199, next); + }, + function (data, next) { + data = data.map(function (nfObject) { + nfObject.value = escape ? validator.escape(String(nfObject.value || '')) : nfObject.value; + return nfObject; + }); + + next(null, data); + }, + ], callback); +}; + +Errors.clear = function (callback) { + db.delete('errors:404', callback); }; diff --git a/src/meta/js.js b/src/meta/js.js index 4858f69bf5..8f483779da 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -10,330 +10,339 @@ var file = require('../file'); var plugins = require('../plugins'); var minifier = require('./minifier'); -module.exports = function (Meta) { - Meta.js = {}; +var JS = module.exports; - Meta.js.scripts = { - base: [ - 'node_modules/jquery/dist/jquery.js', - 'node_modules/socket.io-client/dist/socket.io.js', - 'public/vendor/jquery/timeago/jquery.timeago.js', - 'public/vendor/jquery/js/jquery.form.min.js', - 'public/vendor/visibility/visibility.min.js', - 'node_modules/bootstrap/dist/js/bootstrap.js', - 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js', - 'public/vendor/jquery/textcomplete/jquery.textcomplete.js', - 'public/vendor/requirejs/require.js', - 'public/src/require-config.js', - 'public/vendor/bootbox/bootbox.js', - 'public/vendor/bootbox/wrapper.js', - 'public/vendor/tinycon/tinycon.js', - 'public/vendor/xregexp/xregexp.js', - 'public/vendor/xregexp/unicode/unicode-base.js', - 'node_modules/templates.js/lib/templates.js', - 'public/src/utils.js', - 'public/src/sockets.js', - 'public/src/app.js', - 'public/src/ajaxify.js', - 'public/src/overrides.js', - 'public/src/widgets.js', - 'node_modules/promise-polyfill/promise.js', - ], +JS.scripts = { + base: [ + 'node_modules/jquery/dist/jquery.js', + 'node_modules/socket.io-client/dist/socket.io.js', + 'public/vendor/jquery/timeago/jquery.timeago.js', + 'public/vendor/jquery/js/jquery.form.min.js', + 'public/vendor/visibility/visibility.min.js', + 'node_modules/bootstrap/dist/js/bootstrap.js', + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js', + 'public/vendor/jquery/textcomplete/jquery.textcomplete.js', + 'public/vendor/requirejs/require.js', + 'public/src/require-config.js', + 'public/vendor/bootbox/bootbox.js', + 'public/vendor/bootbox/wrapper.js', + 'public/vendor/tinycon/tinycon.js', + 'public/vendor/xregexp/xregexp.js', + 'public/vendor/xregexp/unicode/unicode-base.js', + 'node_modules/templates.js/lib/templates.js', + 'public/src/utils.js', + 'public/src/sockets.js', + 'public/src/app.js', + 'public/src/ajaxify.js', + 'public/src/overrides.js', + 'public/src/widgets.js', + 'node_modules/promise-polyfill/promise.js', + ], - // files listed below are only available client-side, or are bundled in to reduce # of network requests on cold load - rjs: [ - 'public/src/client/footer.js', - 'public/src/client/chats.js', - 'public/src/client/infinitescroll.js', - 'public/src/client/pagination.js', - 'public/src/client/recent.js', - 'public/src/client/unread.js', - 'public/src/client/topic.js', - 'public/src/client/topic/events.js', - 'public/src/client/topic/fork.js', - 'public/src/client/topic/move.js', - 'public/src/client/topic/posts.js', - 'public/src/client/topic/images.js', - 'public/src/client/topic/postTools.js', - 'public/src/client/topic/threadTools.js', - 'public/src/client/categories.js', - 'public/src/client/category.js', - 'public/src/client/category/tools.js', + // files listed below are only available client-side, or are bundled in to reduce # of network requests on cold load + rjs: [ + 'public/src/client/footer.js', + 'public/src/client/chats.js', + 'public/src/client/infinitescroll.js', + 'public/src/client/pagination.js', + 'public/src/client/recent.js', + 'public/src/client/unread.js', + 'public/src/client/topic.js', + 'public/src/client/topic/events.js', + 'public/src/client/topic/fork.js', + 'public/src/client/topic/move.js', + 'public/src/client/topic/posts.js', + 'public/src/client/topic/images.js', + 'public/src/client/topic/postTools.js', + 'public/src/client/topic/threadTools.js', + 'public/src/client/categories.js', + 'public/src/client/category.js', + 'public/src/client/category/tools.js', - 'public/src/modules/translator.js', - 'public/src/modules/notifications.js', - 'public/src/modules/chat.js', - 'public/src/modules/components.js', - 'public/src/modules/sort.js', - 'public/src/modules/navigator.js', - 'public/src/modules/topicSelect.js', - 'public/src/modules/share.js', - 'public/src/modules/search.js', - 'public/src/modules/alerts.js', - 'public/src/modules/taskbar.js', - 'public/src/modules/helpers.js', - 'public/src/modules/string.js', - 'public/src/modules/flags.js', - 'public/src/modules/storage.js', - ], + 'public/src/modules/translator.js', + 'public/src/modules/notifications.js', + 'public/src/modules/chat.js', + 'public/src/modules/components.js', + 'public/src/modules/sort.js', + 'public/src/modules/navigator.js', + 'public/src/modules/topicSelect.js', + 'public/src/modules/categorySelector.js', + 'public/src/modules/share.js', + 'public/src/modules/search.js', + 'public/src/modules/alerts.js', + 'public/src/modules/taskbar.js', + 'public/src/modules/helpers.js', + 'public/src/modules/string.js', + 'public/src/modules/flags.js', + 'public/src/modules/storage.js', + ], - // modules listed below are built (/src/modules) so they can be defined anonymously - modules: { - 'Chart.js': 'node_modules/chart.js/dist/Chart.min.js', - 'mousetrap.js': 'node_modules/mousetrap/mousetrap.min.js', - 'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js', - 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', - 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', - ace: 'node_modules/ace-builds/src-min', - }, - }; + // modules listed below are built (/src/modules) so they can be defined anonymously + modules: { + 'Chart.js': 'node_modules/chart.js/dist/Chart.min.js', + 'mousetrap.js': 'node_modules/mousetrap/mousetrap.min.js', + 'cropper.js': 'node_modules/cropperjs/dist/cropper.min.js', + 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', + 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', + ace: 'node_modules/ace-builds/src-min', + }, +}; - var basePath = path.resolve(__dirname, '../..'); +var basePath = path.resolve(__dirname, '../..'); - function minifyModules(modules, fork, callback) { - var moduleDirs = modules.reduce(function (prev, mod) { - var dir = path.resolve(path.dirname(mod.destPath)); - if (prev.indexOf(dir) === -1) { - prev.push(dir); +function minifyModules(modules, fork, callback) { + var moduleDirs = modules.reduce(function (prev, mod) { + var dir = path.resolve(path.dirname(mod.destPath)); + if (prev.indexOf(dir) === -1) { + prev.push(dir); + } + return prev; + }, []); + + async.eachLimit(moduleDirs, 1000, mkdirp, function (err) { + if (err) { + return callback(err); + } + + var filtered = modules.reduce(function (prev, mod) { + if (mod.srcPath.endsWith('.min.js') || path.dirname(mod.srcPath).endsWith('min')) { + prev.skip.push(mod); + } else { + prev.minify.push(mod); } + return prev; - }, []); + }, { minify: [], skip: [] }); - async.eachLimit(moduleDirs, 1000, mkdirp, function (err) { - if (err) { - return callback(err); - } - - var filtered = modules.reduce(function (prev, mod) { - if (mod.srcPath.endsWith('.min.js') || path.dirname(mod.srcPath).endsWith('min')) { - prev.skip.push(mod); - } else { - prev.minify.push(mod); - } - - return prev; - }, { minify: [], skip: [] }); - - async.parallel([ - function (cb) { - minifier.js.minifyBatch(filtered.minify, fork, cb); - }, - function (cb) { - async.eachLimit(filtered.skip, 500, function (mod, next) { - file.link(mod.srcPath, mod.destPath, next); - }, cb); - }, - ], callback); - }); - } - - function linkModules(callback) { - var modules = Meta.js.scripts.modules; - - async.eachLimit(Object.keys(modules), 1000, function (relPath, next) { - var srcPath = path.join(__dirname, '../../', modules[relPath]); - var destPath = path.join(__dirname, '../../build/public/src/modules', relPath); - - async.parallel({ - dir: function (cb) { - mkdirp(path.dirname(destPath), function (err) { - cb(err); - }); - }, - stats: function (cb) { - fs.stat(srcPath, cb); - }, - }, function (err, res) { - if (err) { - return next(err); - } - if (res.stats.isDirectory()) { - return file.linkDirs(srcPath, destPath, next); - } - - file.link(srcPath, destPath, next); - }); - }, callback); - } - - var moduleDirs = ['modules', 'admin', 'client']; - - function getModuleList(callback) { - var modules = Object.keys(Meta.js.scripts.modules).map(function (relPath) { - return { - srcPath: path.join(__dirname, '../../', Meta.js.scripts.modules[relPath]), - destPath: path.join(__dirname, '../../build/public/src/modules', relPath), - }; - }); - - var coreDirs = moduleDirs.map(function (dir) { - return { - srcPath: path.join(__dirname, '../../public/src', dir), - destPath: path.join(__dirname, '../../build/public/src', dir), - }; - }); - - modules = modules.concat(coreDirs); - - var moduleFiles = []; - async.eachLimit(modules, 1000, function (module, next) { - var srcPath = module.srcPath; - var destPath = module.destPath; - - fs.stat(srcPath, function (err, stats) { - if (err) { - return next(err); - } - if (!stats.isDirectory()) { - moduleFiles.push(module); - return next(); - } - - file.walk(srcPath, function (err, files) { - if (err) { - return next(err); - } - - var mods = files.filter(function (filePath) { - return path.extname(filePath) === '.js'; - }).map(function (filePath) { - return { - srcPath: path.normalize(filePath), - destPath: path.join(destPath, path.relative(srcPath, filePath)), - }; - }); - - moduleFiles = moduleFiles.concat(mods).map(function (mod) { - mod.filename = path.relative(basePath, mod.srcPath).replace(/\\/g, '/'); - return mod; - }); - - next(); - }); - }); - }, function (err) { - callback(err, moduleFiles); - }); - } - - function clearModules(callback) { - var builtPaths = moduleDirs.map(function (p) { - return path.join(__dirname, '../../build/public/src', p); - }); - async.each(builtPaths, function (builtPath, next) { - rimraf(builtPath, next); - }, function (err) { - callback(err); - }); - } - - Meta.js.buildModules = function (fork, callback) { - async.waterfall([ - clearModules, - function (next) { - if (global.env === 'development') { - return linkModules(callback); - } - - getModuleList(next); + async.parallel([ + function (cb) { + minifier.js.minifyBatch(filtered.minify, fork, cb); }, - function (modules, next) { - minifyModules(modules, fork, next); + function (cb) { + async.eachLimit(filtered.skip, 500, function (mod, next) { + file.link(mod.srcPath, mod.destPath, next); + }, cb); }, ], callback); - }; + }); +} - Meta.js.linkStatics = function (callback) { - rimraf(path.join(__dirname, '../../build/public/plugins'), function (err) { +function linkModules(callback) { + var modules = JS.scripts.modules; + + async.eachLimit(Object.keys(modules), 1000, function (relPath, next) { + var srcPath = path.join(__dirname, '../../', modules[relPath]); + var destPath = path.join(__dirname, '../../build/public/src/modules', relPath); + + async.parallel({ + dir: function (cb) { + mkdirp(path.dirname(destPath), function (err) { + cb(err); + }); + }, + stats: function (cb) { + fs.stat(srcPath, cb); + }, + }, function (err, res) { if (err) { - return callback(err); + return next(err); + } + if (res.stats.isDirectory()) { + return file.linkDirs(srcPath, destPath, next); } - async.eachLimit(Object.keys(plugins.staticDirs), 1000, function (mappedPath, next) { - var sourceDir = plugins.staticDirs[mappedPath]; - var destDir = path.join(__dirname, '../../build/public/plugins', mappedPath); - mkdirp(path.dirname(destDir), function (err) { + if (process.platform === 'win32') { + fs.readFile(srcPath, function (err, file) { if (err) { return next(err); } - file.linkDirs(sourceDir, destDir, next); + fs.writeFile(destPath, file, next); }); - }, callback); + } else { + file.link(srcPath, destPath, next); + } }); - }; + }, callback); +} - function getBundleScriptList(target, callback) { - var pluginDirectories = []; +var moduleDirs = ['modules', 'admin', 'client']; - if (target === 'admin') { - target = 'acp'; - } - var pluginScripts = plugins[target + 'Scripts'].filter(function (path) { - if (path.endsWith('.js')) { - return true; +function getModuleList(callback) { + var modules = Object.keys(JS.scripts.modules).map(function (relPath) { + return { + srcPath: path.join(__dirname, '../../', JS.scripts.modules[relPath]), + destPath: path.join(__dirname, '../../build/public/src/modules', relPath), + }; + }); + + var coreDirs = moduleDirs.map(function (dir) { + return { + srcPath: path.join(__dirname, '../../public/src', dir), + destPath: path.join(__dirname, '../../build/public/src', dir), + }; + }); + + modules = modules.concat(coreDirs); + + var moduleFiles = []; + async.eachLimit(modules, 1000, function (module, next) { + var srcPath = module.srcPath; + var destPath = module.destPath; + + fs.stat(srcPath, function (err, stats) { + if (err) { + return next(err); + } + if (!stats.isDirectory()) { + moduleFiles.push(module); + return next(); } - pluginDirectories.push(path); - return false; - }); - - async.each(pluginDirectories, function (directory, next) { - file.walk(directory, function (err, scripts) { + file.walk(srcPath, function (err, files) { if (err) { return next(err); } - pluginScripts = pluginScripts.concat(scripts); + var mods = files.filter(function (filePath) { + return path.extname(filePath) === '.js'; + }).map(function (filePath) { + return { + srcPath: path.normalize(filePath), + destPath: path.join(destPath, path.relative(srcPath, filePath)), + }; + }); + + moduleFiles = moduleFiles.concat(mods).map(function (mod) { + mod.filename = path.relative(basePath, mod.srcPath).replace(/\\/g, '/'); + return mod; + }); + next(); }); - }, function (err) { - if (err) { - return callback(err); - } - - var scripts = Meta.js.scripts.base.concat(pluginScripts); - - if (target === 'client' && global.env !== 'development') { - scripts = scripts.concat(Meta.js.scripts.rjs); - } - - scripts = scripts.map(function (script) { - var srcPath = path.resolve(basePath, script).replace(/\\/g, '/'); - return { - srcPath: srcPath, - filename: path.relative(basePath, srcPath).replace(/\\/g, '/'), - }; - }); - - callback(null, scripts); }); - } + }, function (err) { + callback(err, moduleFiles); + }); +} - Meta.js.buildBundle = function (target, fork, callback) { - var fileNames = { - client: 'nodebb.min.js', - admin: 'acp.min.js', - }; +function clearModules(callback) { + var builtPaths = moduleDirs.map(function (p) { + return path.join(__dirname, '../../build/public/src', p); + }); + async.each(builtPaths, function (builtPath, next) { + rimraf(builtPath, next); + }, function (err) { + callback(err); + }); +} - async.waterfall([ - function (next) { - getBundleScriptList(target, next); - }, - function (files, next) { - var minify = global.env !== 'development'; - var filePath = path.join(__dirname, '../../build/public', fileNames[target]); +JS.buildModules = function (fork, callback) { + async.waterfall([ + clearModules, + function (next) { + if (global.env === 'development') { + return linkModules(callback); + } - minifier.js.bundle({ - files: files, - filename: fileNames[target], - destPath: filePath, - }, minify, fork, next); - }, - ], callback); - }; - - Meta.js.killMinifier = function () { - minifier.killAll(); - }; + getModuleList(next); + }, + function (modules, next) { + minifyModules(modules, fork, next); + }, + ], callback); +}; + +JS.linkStatics = function (callback) { + rimraf(path.join(__dirname, '../../build/public/plugins'), function (err) { + if (err) { + return callback(err); + } + async.eachLimit(Object.keys(plugins.staticDirs), 1000, function (mappedPath, next) { + var sourceDir = plugins.staticDirs[mappedPath]; + var destDir = path.join(__dirname, '../../build/public/plugins', mappedPath); + + mkdirp(path.dirname(destDir), function (err) { + if (err) { + return next(err); + } + + file.linkDirs(sourceDir, destDir, next); + }); + }, callback); + }); +}; + +function getBundleScriptList(target, callback) { + var pluginDirectories = []; + + if (target === 'admin') { + target = 'acp'; + } + var pluginScripts = plugins[target + 'Scripts'].filter(function (path) { + if (path.endsWith('.js')) { + return true; + } + + pluginDirectories.push(path); + return false; + }); + + async.each(pluginDirectories, function (directory, next) { + file.walk(directory, function (err, scripts) { + if (err) { + return next(err); + } + + pluginScripts = pluginScripts.concat(scripts); + next(); + }); + }, function (err) { + if (err) { + return callback(err); + } + + var scripts = JS.scripts.base.concat(pluginScripts); + + if (target === 'client' && global.env !== 'development') { + scripts = scripts.concat(JS.scripts.rjs); + } + + scripts = scripts.map(function (script) { + var srcPath = path.resolve(basePath, script).replace(/\\/g, '/'); + return { + srcPath: srcPath, + filename: path.relative(basePath, srcPath).replace(/\\/g, '/'), + }; + }); + + callback(null, scripts); + }); +} + +JS.buildBundle = function (target, fork, callback) { + var fileNames = { + client: 'nodebb.min.js', + admin: 'acp.min.js', + }; + + async.waterfall([ + function (next) { + getBundleScriptList(target, next); + }, + function (files, next) { + var minify = global.env !== 'development'; + var filePath = path.join(__dirname, '../../build/public', fileNames[target]); + + minifier.js.bundle({ + files: files, + filename: fileNames[target], + destPath: filePath, + }, minify, fork, next); + }, + ], callback); +}; + +JS.killMinifier = function () { + minifier.killAll(); }; diff --git a/src/meta/languages.js b/src/meta/languages.js index 06c73ace2e..3cf1359f4a 100644 --- a/src/meta/languages.js +++ b/src/meta/languages.js @@ -107,15 +107,15 @@ function getTranslationTree(callback) { // 2. old language string (en_GB) // 3. corrected plugin defaultLang (en-US) // 4. old plugin defaultLang (en_US) - async.eachLimit(plugins, 10, function (pluginData, done) { + async.eachLimit(plugins, 20, function (pluginData, done) { var pluginLanguages = path.join(__dirname, '../../node_modules/', pluginData.id, pluginData.languages); var defaultLang = pluginData.defaultLang || 'en-GB'; - async.some([ - lang, - lang.replace('-', '_').replace('-x-', '@'), - defaultLang.replace('_', '-').replace('@', '-x-'), + async.eachSeries([ defaultLang.replace('-', '_').replace('-x-', '@'), + defaultLang.replace('_', '-').replace('@', '-x-'), + lang.replace('-', '_').replace('-x-', '@'), + lang, ], function (language, next) { fs.readFile(path.join(pluginLanguages, language, namespace + '.json'), function (err, buffer) { if (err) { diff --git a/src/meta/logs.js b/src/meta/logs.js index 30dd983a36..1f950d6057 100644 --- a/src/meta/logs.js +++ b/src/meta/logs.js @@ -3,18 +3,16 @@ var path = require('path'); var fs = require('fs'); -module.exports = function (Meta) { - Meta.logs = { - path: path.join(__dirname, '..', '..', 'logs', 'output.log'), - }; +var Logs = module.exports; - Meta.logs.get = function (callback) { - fs.readFile(Meta.logs.path, { - encoding: 'utf-8', - }, callback); - }; +Logs.path = path.join(__dirname, '..', '..', 'logs', 'output.log'); - Meta.logs.clear = function (callback) { - fs.truncate(Meta.logs.path, 0, callback); - }; +Logs.get = function (callback) { + fs.readFile(Logs.path, { + encoding: 'utf-8', + }, callback); +}; + +Logs.clear = function (callback) { + fs.truncate(Logs.path, 0, callback); }; diff --git a/src/meta/minifier.js b/src/meta/minifier.js index e9b6243d2f..4b64420701 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -11,31 +11,10 @@ var postcss = require('postcss'); var autoprefixer = require('autoprefixer'); var clean = require('postcss-clean'); +var debugParams = require('./debugParams'); + var Minifier = module.exports; -function setupDebugging() { - /** - * Check if the parent process is running with the debug option --debug (or --debug-brk) - */ - var forkProcessParams = {}; - if (global.v8debug || parseInt(process.execArgv.indexOf('--debug'), 10) !== -1) { - /** - * use the line below if you want to debug minifier.js script too (or even --debug-brk option, but - * you'll have to setup your debugger and connect to the forked process) - */ - // forkProcessParams = { execArgv: ['--debug=' + (global.process.debugPort + 1), '--nolazy'] }; - - /** - * otherwise, just clean up --debug/--debug-brk options which are set up by default from the parent one - */ - forkProcessParams = { - execArgv: [], - }; - } - - return forkProcessParams; -} - var pool = []; var free = []; @@ -68,7 +47,7 @@ function getChild() { return free.shift(); } - var forkProcessParams = setupDebugging(); + var forkProcessParams = debugParams(); var proc = childProcess.fork(__filename, [], Object.assign({}, forkProcessParams, { cwd: __dirname, env: { diff --git a/src/meta/settings.js b/src/meta/settings.js index a1d13b248d..d157a4cd95 100644 --- a/src/meta/settings.js +++ b/src/meta/settings.js @@ -4,61 +4,62 @@ var async = require('async'); var db = require('../database'); var plugins = require('../plugins'); +var Meta = require('../meta'); -module.exports = function (Meta) { - Meta.settings = {}; +var Settings = module.exports; - Meta.settings.get = function (hash, callback) { - db.getObject('settings:' + hash, function (err, settings) { - callback(err, settings || {}); - }); - }; - - Meta.settings.getOne = function (hash, field, callback) { - db.getObjectField('settings:' + hash, field, callback); - }; - - Meta.settings.set = function (hash, values, callback) { - async.waterfall([ - function (next) { - db.setObject('settings:' + hash, values, next); - }, - function (next) { - plugins.fireHook('action:settings.set', { - plugin: hash, - settings: values, - }); - - Meta.reloadRequired = true; - next(); - }, - ], callback); - }; - - Meta.settings.setOne = function (hash, field, value, callback) { - db.setObjectField('settings:' + hash, field, value, callback); - }; - - Meta.settings.setOnEmpty = function (hash, values, callback) { - async.waterfall([ - function (next) { - db.getObject('settings:' + hash, next); - }, - function (settings, next) { - settings = settings || {}; - var empty = {}; - Object.keys(values).forEach(function (key) { - if (!settings.hasOwnProperty(key)) { - empty[key] = values[key]; - } - }); - - if (Object.keys(empty).length) { - db.setObject('settings:' + hash, empty, next); - } else { - next(); - } - }, - ], callback); - }; +Settings.get = function (hash, callback) { + db.getObject('settings:' + hash, function (err, settings) { + callback(err, settings || {}); + }); +}; + +Settings.getOne = function (hash, field, callback) { + db.getObjectField('settings:' + hash, field, callback); +}; + +Settings.set = function (hash, values, callback) { + async.waterfall([ + function (next) { + db.setObject('settings:' + hash, values, next); + }, + function (next) { + plugins.fireHook('action:settings.set', { + plugin: hash, + settings: values, + }); + + Meta.reloadRequired = true; + next(); + }, + ], callback); +}; + +Settings.setOne = function (hash, field, value, callback) { + var data = {}; + data[field] = value; + Settings.set(hash, data, callback); +}; + +Settings.setOnEmpty = function (hash, values, callback) { + async.waterfall([ + function (next) { + db.getObject('settings:' + hash, next); + }, + function (settings, next) { + settings = settings || {}; + var empty = {}; + Object.keys(values).forEach(function (key) { + if (!settings.hasOwnProperty(key)) { + empty[key] = values[key]; + } + }); + + if (Object.keys(empty).length) { + Settings.set(hash, empty, next); + } else { + next(); + } + }, + ], callback); }; diff --git a/src/meta/sounds.js b/src/meta/sounds.js index e7bb7c3599..ec89b471da 100644 --- a/src/meta/sounds.js +++ b/src/meta/sounds.js @@ -9,125 +9,124 @@ var async = require('async'); var file = require('../file'); var plugins = require('../plugins'); var user = require('../user'); +var Meta = require('../meta'); var soundsPath = path.join(__dirname, '../../build/public/sounds'); var uploadsPath = path.join(__dirname, '../../public/uploads/sounds'); -module.exports = function (Meta) { - Meta.sounds = {}; +var Sounds = module.exports; - Meta.sounds.addUploads = function addUploads(callback) { - fs.readdir(uploadsPath, function (err, files) { - if (err) { - if (err.code !== 'ENOENT') { - return callback(err); - } - - files = []; +Sounds.addUploads = function addUploads(callback) { + fs.readdir(uploadsPath, function (err, files) { + if (err) { + if (err.code !== 'ENOENT') { + return callback(err); } - var uploadSounds = files.reduce(function (prev, fileName) { - var name = fileName.split('.'); - if (!name.length || !name[0].length) { - return prev; - } - name = name[0]; - name = name[0].toUpperCase() + name.slice(1); + files = []; + } - prev[name] = fileName; + var uploadSounds = files.reduce(function (prev, fileName) { + var name = fileName.split('.'); + if (!name.length || !name[0].length) { + return prev; + } + name = name[0]; + name = name[0].toUpperCase() + name.slice(1); + + prev[name] = fileName; + return prev; + }, {}); + + plugins.soundpacks = plugins.soundpacks.filter(function (pack) { + return pack.name !== 'Uploads'; + }); + if (Object.keys(uploadSounds).length) { + plugins.soundpacks.push({ + name: 'Uploads', + id: 'uploads', + dir: uploadsPath, + sounds: uploadSounds, + }); + } + + callback(); + }); +}; + +Sounds.build = function build(callback) { + Sounds.addUploads(function (err) { + if (err) { + return callback(err); + } + + var map = plugins.soundpacks.map(function (pack) { + return Object.keys(pack.sounds).reduce(function (prev, soundName) { + var soundPath = pack.sounds[soundName]; + prev[pack.name + ' | ' + soundName] = pack.id + '/' + soundPath; return prev; }, {}); - - plugins.soundpacks = plugins.soundpacks.filter(function (pack) { - return pack.name !== 'Uploads'; - }); - if (Object.keys(uploadSounds).length) { - plugins.soundpacks.push({ - name: 'Uploads', - id: 'uploads', - dir: uploadsPath, - sounds: uploadSounds, - }); - } - - callback(); }); - }; + map.unshift({}); + map = Object.assign.apply(null, map); - Meta.sounds.build = function build(callback) { - Meta.sounds.addUploads(function (err) { - if (err) { - return callback(err); - } - - var map = plugins.soundpacks.map(function (pack) { - return Object.keys(pack.sounds).reduce(function (prev, soundName) { - var soundPath = pack.sounds[soundName]; - prev[pack.name + ' | ' + soundName] = pack.id + '/' + soundPath; - return prev; - }, {}); - }); - map.unshift({}); - map = Object.assign.apply(null, map); - - async.series([ - function (next) { - rimraf(soundsPath, next); - }, - function (next) { - mkdirp(soundsPath, next); - }, - function (cb) { - async.parallel([ - function (next) { - fs.writeFile(path.join(soundsPath, 'fileMap.json'), JSON.stringify(map), next); - }, - function (next) { - async.each(plugins.soundpacks, function (pack, next) { - file.linkDirs(pack.dir, path.join(soundsPath, pack.id), next); - }, next); - }, - ], cb); - }, - ], function (err) { - callback(err); - }); - }); - }; - - var keys = ['chat-incoming', 'chat-outgoing', 'notification']; - - Meta.sounds.getUserSoundMap = function getUserSoundMap(uid, callback) { - async.parallel({ - defaultMapping: function (next) { - Meta.configs.getFields(keys, next); + async.series([ + function (next) { + rimraf(soundsPath, next); }, - userSettings: function (next) { - user.getSettings(uid, next); + function (next) { + mkdirp(soundsPath, next); }, - }, function (err, results) { - if (err) { - return callback(err); - } - - var userSettings = results.userSettings; - userSettings = { - notification: userSettings.notificationSound, - 'chat-incoming': userSettings.incomingChatSound, - 'chat-outgoing': userSettings.outgoingChatSound, - }; - var defaultMapping = results.defaultMapping || {}; - var soundMapping = {}; - - keys.forEach(function (key) { - if (userSettings[key] || userSettings[key] === '') { - soundMapping[key] = userSettings[key] || ''; - } else { - soundMapping[key] = defaultMapping[key] || ''; - } - }); - - callback(null, soundMapping); + function (cb) { + async.parallel([ + function (next) { + fs.writeFile(path.join(soundsPath, 'fileMap.json'), JSON.stringify(map), next); + }, + function (next) { + async.each(plugins.soundpacks, function (pack, next) { + file.linkDirs(pack.dir, path.join(soundsPath, pack.id), next); + }, next); + }, + ], cb); + }, + ], function (err) { + callback(err); }); - }; + }); +}; + +var keys = ['chat-incoming', 'chat-outgoing', 'notification']; + +Sounds.getUserSoundMap = function getUserSoundMap(uid, callback) { + async.parallel({ + defaultMapping: function (next) { + Meta.configs.getFields(keys, next); + }, + userSettings: function (next) { + user.getSettings(uid, next); + }, + }, function (err, results) { + if (err) { + return callback(err); + } + + var userSettings = results.userSettings; + userSettings = { + notification: userSettings.notificationSound, + 'chat-incoming': userSettings.incomingChatSound, + 'chat-outgoing': userSettings.outgoingChatSound, + }; + var defaultMapping = results.defaultMapping || {}; + var soundMapping = {}; + + keys.forEach(function (key) { + if (userSettings[key] || userSettings[key] === '') { + soundMapping[key] = userSettings[key] || ''; + } else { + soundMapping[key] = defaultMapping[key] || ''; + } + }); + + callback(null, soundMapping); + }); }; diff --git a/src/meta/tags.js b/src/meta/tags.js index ac0b395a23..34ed3c43a9 100644 --- a/src/meta/tags.js +++ b/src/meta/tags.js @@ -4,163 +4,163 @@ var nconf = require('nconf'); var validator = require('validator'); var async = require('async'); var winston = require('winston'); + var plugins = require('../plugins'); +var Meta = require('../meta'); -module.exports = function (Meta) { - Meta.tags = {}; +var Tags = module.exports; - Meta.tags.parse = function (req, meta, link, callback) { - async.parallel({ - tags: function (next) { - var defaultTags = [{ - name: 'viewport', - content: 'width=device-width, initial-scale=1.0', - }, { - name: 'content-type', - content: 'text/html; charset=UTF-8', +Tags.parse = function (req, meta, link, callback) { + async.parallel({ + tags: function (next) { + var defaultTags = [{ + name: 'viewport', + content: 'width=device-width, initial-scale=1.0', + }, { + name: 'content-type', + content: 'text/html; charset=UTF-8', + noEscape: true, + }, { + name: 'apple-mobile-web-app-capable', + content: 'yes', + }, { + name: 'mobile-web-app-capable', + content: 'yes', + }, { + property: 'og:site_name', + content: Meta.config.title || 'NodeBB', + }, { + name: 'msapplication-badge', + content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml', + noEscape: true, + }]; + + if (Meta.config.keywords) { + defaultTags.push({ + name: 'keywords', + content: Meta.config.keywords, + }); + } + + if (Meta.config['brand:logo']) { + defaultTags.push({ + name: 'msapplication-square150x150logo', + content: Meta.config['brand:logo'], noEscape: true, - }, { - name: 'apple-mobile-web-app-capable', - content: 'yes', - }, { - name: 'mobile-web-app-capable', - content: 'yes', - }, { - property: 'og:site_name', - content: Meta.config.title || 'NodeBB', - }, { - name: 'msapplication-badge', - content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml', - noEscape: true, - }]; + }); + } - if (Meta.config.keywords) { - defaultTags.push({ - name: 'keywords', - content: Meta.config.keywords, - }); - } + plugins.fireHook('filter:meta.getMetaTags', defaultTags, next); + }, + links: function (next) { + var defaultLinks = [{ + rel: 'icon', + type: 'image/x-icon', + href: nconf.get('relative_path') + '/favicon.ico' + (Meta.config['cache-buster'] ? '?' + Meta.config['cache-buster'] : ''), + }, { + rel: 'manifest', + href: nconf.get('relative_path') + '/manifest.json', + }]; - if (Meta.config['brand:logo']) { - defaultTags.push({ - name: 'msapplication-square150x150logo', - content: Meta.config['brand:logo'], - noEscape: true, - }); - } + if (plugins.hasListeners('filter:search.query')) { + defaultLinks.push({ + rel: 'search', + type: 'application/opensearchdescription+xml', + href: nconf.get('relative_path') + '/osd.xml', + }); + } - plugins.fireHook('filter:meta.getMetaTags', defaultTags, next); - }, - links: function (next) { - var defaultLinks = [{ + // Touch icons for mobile-devices + if (Meta.config['brand:touchIcon']) { + defaultLinks.push({ + rel: 'apple-touch-icon', + href: nconf.get('relative_path') + '/apple-touch-icon', + }, { rel: 'icon', - type: 'image/x-icon', - href: nconf.get('relative_path') + '/favicon.ico' + (Meta.config['cache-buster'] ? '?' + Meta.config['cache-buster'] : ''), + sizes: '36x36', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-36.png', }, { - rel: 'manifest', - href: nconf.get('relative_path') + '/manifest.json', - }]; - - if (plugins.hasListeners('filter:search.query')) { - defaultLinks.push({ - rel: 'search', - type: 'application/opensearchdescription+xml', - href: nconf.get('relative_path') + '/osd.xml', - }); - } - - // Touch icons for mobile-devices - if (Meta.config['brand:touchIcon']) { - defaultLinks.push({ - rel: 'apple-touch-icon', - href: nconf.get('relative_path') + '/apple-touch-icon', - }, { - rel: 'icon', - sizes: '36x36', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-36.png', - }, { - rel: 'icon', - sizes: '48x48', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-48.png', - }, { - rel: 'icon', - sizes: '72x72', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-72.png', - }, { - rel: 'icon', - sizes: '96x96', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-96.png', - }, { - rel: 'icon', - sizes: '144x144', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-144.png', - }, { - rel: 'icon', - sizes: '192x192', - href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-192.png', - }); - } - plugins.fireHook('filter:meta.getLinkTags', defaultLinks, next); - }, - }, function (err, results) { - if (err) { - return callback(err); + rel: 'icon', + sizes: '48x48', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-48.png', + }, { + rel: 'icon', + sizes: '72x72', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-72.png', + }, { + rel: 'icon', + sizes: '96x96', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-96.png', + }, { + rel: 'icon', + sizes: '144x144', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-144.png', + }, { + rel: 'icon', + sizes: '192x192', + href: nconf.get('relative_path') + '/assets/uploads/system/touchicon-192.png', + }); } - - meta = results.tags.concat(meta || []).map(function (tag) { - if (!tag || typeof tag.content !== 'string') { - winston.warn('Invalid meta tag. ', tag); - return tag; - } - - if (!tag.noEscape) { - tag.content = validator.escape(String(tag.content)); - } - - return tag; - }); - - addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); - - var ogUrl = nconf.get('url') + req.path; - addIfNotExists(meta, 'property', 'og:url', ogUrl); - - addIfNotExists(meta, 'name', 'description', Meta.config.description); - addIfNotExists(meta, 'property', 'og:description', Meta.config.description); - - var ogImage = Meta.config['og:image'] || Meta.config['brand:logo'] || ''; - if (ogImage && !ogImage.startsWith('http')) { - ogImage = nconf.get('url') + ogImage; - } - addIfNotExists(meta, 'property', 'og:image', ogImage); - if (ogImage) { - addIfNotExists(meta, 'property', 'og:image:width', 200); - addIfNotExists(meta, 'property', 'og:image:height', 200); - } - - link = results.links.concat(link || []); - - callback(null, { - meta: meta, - link: link, - }); - }); - }; - - function addIfNotExists(meta, keyName, tagName, value) { - var exists = false; - meta.forEach(function (tag) { - if (tag[keyName] === tagName) { - exists = true; - } - }); - - if (!exists && value) { - var data = { - content: validator.escape(String(value)), - }; - data[keyName] = tagName; - meta.push(data); + plugins.fireHook('filter:meta.getLinkTags', defaultLinks, next); + }, + }, function (err, results) { + if (err) { + return callback(err); } - } + + meta = results.tags.concat(meta || []).map(function (tag) { + if (!tag || typeof tag.content !== 'string') { + winston.warn('Invalid meta tag. ', tag); + return tag; + } + + if (!tag.noEscape) { + tag.content = validator.escape(String(tag.content)); + } + + return tag; + }); + + addIfNotExists(meta, 'property', 'og:title', Meta.config.title || 'NodeBB'); + + var ogUrl = nconf.get('url') + req.path; + addIfNotExists(meta, 'property', 'og:url', ogUrl); + + addIfNotExists(meta, 'name', 'description', Meta.config.description); + addIfNotExists(meta, 'property', 'og:description', Meta.config.description); + + var ogImage = Meta.config['og:image'] || Meta.config['brand:logo'] || ''; + if (ogImage && !ogImage.startsWith('http')) { + ogImage = nconf.get('url') + ogImage; + } + addIfNotExists(meta, 'property', 'og:image', ogImage); + if (ogImage) { + addIfNotExists(meta, 'property', 'og:image:width', 200); + addIfNotExists(meta, 'property', 'og:image:height', 200); + } + + link = results.links.concat(link || []); + + callback(null, { + meta: meta, + link: link, + }); + }); }; + +function addIfNotExists(meta, keyName, tagName, value) { + var exists = false; + meta.forEach(function (tag) { + if (tag[keyName] === tagName) { + exists = true; + } + }); + + if (!exists && value) { + var data = { + content: validator.escape(String(value)), + }; + data[keyName] = tagName; + meta.push(data); + } +} diff --git a/src/meta/themes.js b/src/meta/themes.js index 9bca68c431..2ebc091036 100644 --- a/src/meta/themes.js +++ b/src/meta/themes.js @@ -9,168 +9,176 @@ var async = require('async'); var file = require('../file'); var db = require('../database'); +var Meta = require('../meta'); +var events = require('../events'); -module.exports = function (Meta) { - Meta.themes = {}; +var Themes = module.exports; - Meta.themes.get = function (callback) { - var themePath = nconf.get('themes_path'); - if (typeof themePath !== 'string') { - return callback(null, []); - } +Themes.get = function (callback) { + var themePath = nconf.get('themes_path'); + if (typeof themePath !== 'string') { + return callback(null, []); + } - async.waterfall([ - function (next) { - fs.readdir(themePath, next); - }, - function (files, next) { - async.filter(files, function (file, next) { - fs.stat(path.join(themePath, file), function (err, fileStat) { - if (err) { - if (err.code === 'ENOENT') { - return next(null, false); - } - return next(err); + async.waterfall([ + function (next) { + fs.readdir(themePath, next); + }, + function (files, next) { + async.filter(files, function (file, next) { + fs.stat(path.join(themePath, file), function (err, fileStat) { + if (err) { + if (err.code === 'ENOENT') { + return next(null, false); } + return next(err); + } - next(null, (fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); - }); - }, next); - }, - function (themes, next) { - async.map(themes, function (theme, next) { - var config = path.join(themePath, theme, 'theme.json'); + next(null, (fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-')); + }); + }, next); + }, + function (themes, next) { + async.map(themes, function (theme, next) { + var config = path.join(themePath, theme, 'theme.json'); - fs.readFile(config, function (err, file) { - if (err) { - if (err.code === 'ENOENT') { - return next(null, null); - } - return next(err); + fs.readFile(config, function (err, file) { + if (err) { + if (err.code === 'ENOENT') { + return next(null, null); } - try { - var configObj = JSON.parse(file.toString()); + return next(err); + } + try { + var configObj = JSON.parse(file.toString()); - // Minor adjustments for API output - configObj.type = 'local'; - if (configObj.screenshot) { - configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id; - } else { - configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png'; - } - next(null, configObj); - } catch (err) { - winston.error('[themes] Unable to parse theme.json ' + theme); - next(null, null); - } - }); - }, next); - }, - function (themes, next) { - themes = themes.filter(Boolean); - next(null, themes); - }, - ], callback); - }; - - Meta.themes.set = function (data, callback) { - var themeData = { - 'theme:type': data.type, - 'theme:id': data.id, - 'theme:staticDir': '', - 'theme:templates': '', - 'theme:src': '', - }; - - switch (data.type) { - case 'local': - async.waterfall([ - async.apply(Meta.configs.get, 'theme:id'), - function (current, next) { - async.series([ - async.apply(db.sortedSetRemove, 'plugins:active', current), - async.apply(db.sortedSetAdd, 'plugins:active', 0, data.id), - ], function (err) { - next(err); - }); - }, - function (next) { - fs.readFile(path.join(nconf.get('themes_path'), data.id, 'theme.json'), function (err, config) { - if (!err) { - config = JSON.parse(config.toString()); - next(null, config); + // Minor adjustments for API output + configObj.type = 'local'; + if (configObj.screenshot) { + configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id; } else { - next(err); + configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png'; } - }); - }, - function (config, next) { - themeData['theme:staticDir'] = config.staticDir ? config.staticDir : ''; - themeData['theme:templates'] = config.templates ? config.templates : ''; - themeData['theme:src'] = ''; + next(null, configObj); + } catch (err) { + winston.error('[themes] Unable to parse theme.json ' + theme); + next(null, null); + } + }); + }, next); + }, + function (themes, next) { + themes = themes.filter(Boolean); + next(null, themes); + }, + ], callback); +}; - Meta.configs.setMultiple(themeData, next); - - // Re-set the themes path (for when NodeBB is reloaded) - Meta.themes.setPath(config); - }, - ], callback); - - Meta.reloadRequired = true; - break; - - case 'bootswatch': - Meta.configs.setMultiple({ - 'theme:src': data.src, - bootswatchSkin: data.id.toLowerCase(), - }, callback); - break; - } +Themes.set = function (data, callback) { + var themeData = { + 'theme:type': data.type, + 'theme:id': data.id, + 'theme:staticDir': '', + 'theme:templates': '', + 'theme:src': '', }; - Meta.themes.setupPaths = function (callback) { + switch (data.type) { + case 'local': async.waterfall([ + async.apply(Meta.configs.get, 'theme:id'), + function (current, next) { + async.series([ + async.apply(db.sortedSetRemove, 'plugins:active', current), + async.apply(db.sortedSetAdd, 'plugins:active', 0, data.id), + ], function (err) { + next(err); + }); + }, function (next) { - async.parallel({ - themesData: Meta.themes.get, - currentThemeId: function (next) { - db.getObjectField('config', 'theme:id', next); - }, + fs.readFile(path.join(nconf.get('themes_path'), data.id, 'theme.json'), function (err, config) { + if (!err) { + config = JSON.parse(config.toString()); + next(null, config); + } else { + next(err); + } + }); + }, + function (config, next) { + themeData['theme:staticDir'] = config.staticDir ? config.staticDir : ''; + themeData['theme:templates'] = config.templates ? config.templates : ''; + themeData['theme:src'] = ''; + + Meta.configs.setMultiple(themeData, next); + + // Re-set the themes path (for when NodeBB is reloaded) + Themes.setPath(config); + }, + function (next) { + events.log({ + type: 'theme-set', + uid: parseInt(data.uid, 10) || 0, + ip: data.ip || '127.0.0.1', + text: data.id, }, next); }, - function (data, next) { - var themeId = data.currentThemeId || 'nodebb-theme-persona'; - - var themeObj = data.themesData.filter(function (themeObj) { - return themeObj.id === themeId; - })[0]; - - if (process.env.NODE_ENV === 'development') { - winston.info('[themes] Using theme ' + themeId); - } - - if (!themeObj) { - return callback(new Error('[[error:theme-not-found]]')); - } - - Meta.themes.setPath(themeObj); - next(); - }, ], callback); - }; - Meta.themes.setPath = function (themeObj) { - // Theme's templates path - var themePath = nconf.get('base_templates_path'); - var fallback = path.join(nconf.get('themes_path'), themeObj.id, 'templates'); + Meta.reloadRequired = true; + break; - if (themeObj.templates) { - themePath = path.join(nconf.get('themes_path'), themeObj.id, themeObj.templates); - } else if (file.existsSync(fallback)) { - themePath = fallback; - } - - nconf.set('theme_templates_path', themePath); - nconf.set('theme_config', path.join(nconf.get('themes_path'), themeObj.id, 'theme.json')); - }; + case 'bootswatch': + Meta.configs.setMultiple({ + 'theme:src': data.src, + bootswatchSkin: data.id.toLowerCase(), + }, callback); + break; + } +}; + +Themes.setupPaths = function (callback) { + async.waterfall([ + function (next) { + async.parallel({ + themesData: Themes.get, + currentThemeId: function (next) { + db.getObjectField('config', 'theme:id', next); + }, + }, next); + }, + function (data, next) { + var themeId = data.currentThemeId || 'nodebb-theme-persona'; + + var themeObj = data.themesData.filter(function (themeObj) { + return themeObj.id === themeId; + })[0]; + + if (process.env.NODE_ENV === 'development') { + winston.info('[themes] Using theme ' + themeId); + } + + if (!themeObj) { + return callback(new Error('[[error:theme-not-found]]')); + } + + Themes.setPath(themeObj); + next(); + }, + ], callback); +}; + +Themes.setPath = function (themeObj) { + // Theme's templates path + var themePath = nconf.get('base_templates_path'); + var fallback = path.join(nconf.get('themes_path'), themeObj.id, 'templates'); + + if (themeObj.templates) { + themePath = path.join(nconf.get('themes_path'), themeObj.id, themeObj.templates); + } else if (file.existsSync(fallback)) { + themePath = fallback; + } + + nconf.set('theme_templates_path', themePath); + nconf.set('theme_config', path.join(nconf.get('themes_path'), themeObj.id, 'theme.json')); }; diff --git a/src/middleware/header.js b/src/middleware/header.js index deb8d87cac..fe95b11a27 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -8,6 +8,8 @@ var user = require('../user'); var meta = require('../meta'); var plugins = require('../plugins'); var navigation = require('../navigation'); +var translator = require('../translator'); +var utils = require('../utils'); var controllers = { api: require('../controllers/api'), @@ -97,6 +99,11 @@ module.exports = function (middleware) { } db.get('uid:' + req.uid + ':confirm:email:sent', next); }, + languageDirection: function (next) { + translator.translate('[[language:dir]]', res.locals.config.userLang, function (translated) { + next(null, translated); + }); + }, navigation: async.apply(navigation.get), tags: async.apply(meta.tags.parse, req, res.locals.metaTags, res.locals.linkTags), banned: async.apply(user.isBanned, req.uid), @@ -135,6 +142,7 @@ module.exports = function (middleware) { templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin; templateValues.defaultLang = meta.config.defaultLang || 'en-GB'; templateValues.userLang = res.locals.config.userLang; + templateValues.languageDirection = results.languageDirection; templateValues.privateUserInfo = parseInt(meta.config.privateUserInfo, 10) === 1; templateValues.privateTagListing = parseInt(meta.config.privateTagListing, 10) === 1; @@ -145,6 +153,8 @@ module.exports = function (middleware) { return { src: script }; }); + addTimeagoLocaleScript(templateValues.scripts, res.locals.config.userLang); + if (req.route && req.route.path === '/') { modifyTitle(templateValues); } @@ -161,6 +171,11 @@ module.exports = function (middleware) { ], callback); }; + function addTimeagoLocaleScript(scripts, userLang) { + var languageCode = utils.userLangToTimeagoCode(userLang); + scripts.push({ src: nconf.get('relative_path') + '/assets/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js' }); + } + middleware.renderFooter = function (req, res, data, callback) { async.waterfall([ function (next) { diff --git a/src/middleware/index.js b/src/middleware/index.js index f196ad7e54..2b39184b86 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -83,7 +83,13 @@ middleware.routeTouchIcon = function (req, res) { if (meta.config['brand:touchIcon'] && validator.isURL(meta.config['brand:touchIcon'])) { return res.redirect(meta.config['brand:touchIcon']); } - return res.sendFile(path.join(__dirname, '../../public', meta.config['brand:touchIcon'] || '/logo.png'), { + var iconPath = '../../public'; + if (meta.config['brand:touchIcon']) { + iconPath += meta.config['brand:touchIcon'].replace(/assets\/uploads/, 'uploads'); + } else { + iconPath += '/logo.png'; + } + return res.sendFile(path.join(__dirname, iconPath), { maxAge: req.app.enabled('cache') ? 5184000000 : 0, }); }; @@ -123,7 +129,7 @@ middleware.privateUploads = function (req, res, next) { if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) { return next(); } - if (req.path.startsWith('/assets/uploads/files')) { + if (req.path.startsWith(nconf.get('relative_path') + '/assets/uploads/files')) { return res.status(403).json('not-allowed'); } next(); diff --git a/src/middleware/render.js b/src/middleware/render.js index 366f6f9f2a..e37b994445 100644 --- a/src/middleware/render.js +++ b/src/middleware/render.js @@ -44,7 +44,7 @@ module.exports = function (middleware) { plugins.fireHook('filter:' + template + '.build', { req: req, res: res, templateData: options }, next); }, function (data, next) { - plugins.fireHook('filter:middleware.render', { req: res, res: res, templateData: data.templateData }, next); + plugins.fireHook('filter:middleware.render', { req: req, res: res, templateData: data.templateData }, next); }, function (data, next) { options = data.templateData; diff --git a/src/notifications.js b/src/notifications.js index b11e7ed392..58ccf2e8fa 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -160,9 +160,7 @@ Notifications.push = function (notification, uids, callback) { uids = [uids]; } - uids = uids.filter(function (uid, index, array) { - return parseInt(uid, 10) && array.indexOf(uid) === index; - }); + uids = _.uniq(uids); if (!uids.length) { return callback(); @@ -292,7 +290,9 @@ Notifications.markUnread = function (nid, uid, callback) { async.apply(db.sortedSetAdd, 'uid:' + uid + ':notifications:unread', notification.datetime, nid), ], next); }, - ], callback); + ], function (err) { + callback(err); + }); }; Notifications.markReadMultiple = function (nids, uid, callback) { diff --git a/src/password.js b/src/password.js index 76123ed974..137382c993 100644 --- a/src/password.js +++ b/src/password.js @@ -1,9 +1,10 @@ 'use strict'; - var fork = require('child_process').fork; var path = require('path'); +var debugParams = require('./meta/debugParams'); + exports.hash = function (rounds, password, callback) { forkChild({ type: 'hash', rounds: rounds, password: password }, callback); }; @@ -16,11 +17,7 @@ exports.compare = function (password, hash, callback) { }; function forkChild(message, callback) { - var forkProcessParams = {}; - if (global.v8debug || parseInt(process.execArgv.indexOf('--debug'), 10) !== -1) { - forkProcessParams = { execArgv: ['--debug=' + (5859), '--nolazy'] }; - } - var child = fork(path.join(__dirname, 'bcrypt'), [], forkProcessParams); + var child = fork(path.join(__dirname, 'bcrypt'), [], debugParams()); child.on('message', function (msg) { if (msg.err) { diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 16ceb32878..40684e31ab 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -9,6 +9,8 @@ module.exports = function (Plugins) { 'filter:post.save': 'filter:post.create', 'filter:user.profileLinks': 'filter:user.profileMenu', 'action:post.flag': 'action:flag.create', + 'action:flag.create': 'action:flags.create', + 'action:flag.update': 'action:flags.update', }; /* `data` is an object consisting of (* is required): diff --git a/src/plugins/install.js b/src/plugins/install.js index a5ba2db1dd..ced9f800f8 100644 --- a/src/plugins/install.js +++ b/src/plugins/install.js @@ -10,7 +10,7 @@ var os = require('os'); var db = require('../database'); var meta = require('../meta'); var pubsub = require('../pubsub'); - +var events = require('../events'); module.exports = function (Plugins) { if (nconf.get('isPrimary') === 'true') { @@ -52,6 +52,12 @@ module.exports = function (Plugins) { Plugins.fireHook(isActive ? 'action:plugin.deactivate' : 'action:plugin.activate', { id: id }); setImmediate(next); }, + function (next) { + events.log({ + type: 'plugin-' + (isActive ? 'deactivate' : 'activate'), + text: id, + }, next); + }, ], function (err) { if (err) { winston.warn('[plugins] Could not toggle active state on plugin \'' + id + '\''); @@ -101,7 +107,7 @@ module.exports = function (Plugins) { } function runNpmCommand(command, pkgName, version, callback) { - require('child_process').execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', [command, pkgName + (command === 'install' ? '@' + version : '')], function (err, stdout) { + require('child_process').execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', [command, pkgName + (command === 'install' ? '@' + version : ''), '--no-save'], function (err, stdout) { if (err) { return callback(err); } diff --git a/src/posts/category.js b/src/posts/category.js index 42319fd6ed..d1de31850e 100644 --- a/src/posts/category.js +++ b/src/posts/category.js @@ -28,11 +28,9 @@ module.exports = function (Posts) { }, function (_postData, next) { postData = _postData; - tids = postData.map(function (post) { - return post.tid; - }).filter(function (tid, index, array) { - return tid && array.indexOf(tid) === index; - }); + tids = _.uniq(postData.map(function (post) { + return post && post.tid; + }).filter(Boolean)); topics.getTopicsFields(tids, ['cid'], next); }, diff --git a/src/posts/delete.js b/src/posts/delete.js index 9b40f3ede5..44ffb12d87 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -146,7 +146,7 @@ module.exports = function (Posts) { deletePostFromGroups(pid, next); }, function (next) { - db.sortedSetsRemove(['posts:pid', 'posts:flagged'], pid, next); + db.sortedSetsRemove(['posts:pid', 'posts:votes', 'posts:flagged'], pid, next); }, ], function (err) { next(err); diff --git a/src/posts/edit.js b/src/posts/edit.js index 65ca05fc2f..0a9867feeb 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -48,9 +48,7 @@ module.exports = function (Posts) { }, function (result, next) { postData = result.post; - Posts.setPostFields(data.pid, postData, next); - }, - function (next) { + async.parallel({ editor: function (next) { user.getUserFields(data.uid, ['username', 'userslug'], next); @@ -62,7 +60,9 @@ module.exports = function (Posts) { }, function (_results, next) { results = _results; - + Posts.setPostFields(data.pid, postData, next); + }, + function (next) { postData.cid = results.topic.cid; postData.topic = results.topic; plugins.fireHook('action:post.edit', { post: _.clone(postData), uid: data.uid }); @@ -123,6 +123,17 @@ module.exports = function (Posts) { data.tags = data.tags || []; + if (!data.tags.length) { + return next(null, true); + } + + privileges.categories.can('topics:tag', topicData.cid, data.uid, next); + }, + function (canTag, next) { + if (!canTag) { + return next(new Error('[[error:no-privileges]]')); + } + plugins.fireHook('filter:topic.edit', { req: data.req, topic: topicData, data: data }, next); }, function (results, next) { diff --git a/src/privileges.js b/src/privileges.js index 63bae570e1..c1ac018ec7 100644 --- a/src/privileges.js +++ b/src/privileges.js @@ -2,12 +2,29 @@ var privileges = module.exports; +privileges.privilegeLabels = [ + { name: 'Find Category' }, + { name: 'Access Category' }, + { name: 'Access Topics' }, + { name: 'Create Topics' }, + { name: 'Reply to Topics' }, + { name: 'Tag Topics' }, + { name: 'Edit Posts' }, + { name: 'Delete Posts' }, + { name: 'Delete Topics' }, + { name: 'Upload Images' }, + { name: 'Upload Files' }, + { name: 'Purge' }, + { name: 'Moderate' }, +]; + privileges.userPrivilegeList = [ 'find', 'read', 'topics:read', 'topics:create', 'topics:reply', + 'topics:tag', 'posts:edit', 'posts:delete', 'topics:delete', diff --git a/src/privileges/categories.js b/src/privileges/categories.js index 977485e976..97889c2b2e 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -16,28 +16,13 @@ module.exports = function (privileges) { privileges.categories.list = function (cid, callback) { // Method used in admin/category controller to show all users/groups with privs in that given cid - var privilegeLabels = [ - { name: 'Find Category' }, - { name: 'Access Category' }, - { name: 'Access Topics' }, - { name: 'Create Topics' }, - { name: 'Reply to Topics' }, - { name: 'Edit Posts' }, - { name: 'Delete Posts' }, - { name: 'Delete Topics' }, - { name: 'Upload Images' }, - { name: 'Upload Files' }, - { name: 'Purge' }, - { name: 'Moderate' }, - ]; - async.waterfall([ function (next) { async.parallel({ labels: function (next) { async.parallel({ - users: async.apply(plugins.fireHook, 'filter:privileges.list_human', privilegeLabels), - groups: async.apply(plugins.fireHook, 'filter:privileges.groups.list_human', privilegeLabels), + users: async.apply(plugins.fireHook, 'filter:privileges.list_human', privileges.privilegeLabels), + groups: async.apply(plugins.fireHook, 'filter:privileges.groups.list_human', privileges.privilegeLabels), }, next); }, users: function (next) { @@ -155,7 +140,7 @@ module.exports = function (privileges) { }; privileges.categories.get = function (cid, uid, callback) { - var privs = ['topics:create', 'topics:read', 'read']; + var privs = ['topics:create', 'topics:read', 'topics:tag', 'read']; async.waterfall([ function (next) { async.parallel({ @@ -177,6 +162,7 @@ module.exports = function (privileges) { plugins.fireHook('filter:privileges.categories.get', { 'topics:create': privData['topics:create'] || isAdminOrMod, 'topics:read': privData['topics:read'] || isAdminOrMod, + 'topics:tag': privData['topics:tag'] || isAdminOrMod, read: privData.read || isAdminOrMod, cid: cid, uid: uid, @@ -246,9 +232,7 @@ module.exports = function (privileges) { return callback(null, []); } - cids = cids.filter(function (cid, index, array) { - return array.indexOf(cid) === index; - }); + cids = _.uniq(cids); async.waterfall([ function (next) { @@ -287,9 +271,7 @@ module.exports = function (privileges) { return callback(null, []); } - uids = uids.filter(function (uid, index, array) { - return array.indexOf(uid) === index; - }); + uids = _.uniq(uids); async.waterfall([ function (next) { @@ -300,14 +282,14 @@ module.exports = function (privileges) { isModerators: function (next) { user.isModerator(uids, cid, next); }, - isAdmin: function (next) { + isAdmins: function (next) { user.isAdministrator(uids, next); }, }, next); }, function (results, next) { uids = uids.filter(function (uid, index) { - return results.allowedTo[index] || results.isModerators[index] || results.isAdmin[index]; + return results.allowedTo[index] || results.isModerators[index] || results.isAdmins[index]; }); next(null, uids); }, @@ -350,59 +332,22 @@ module.exports = function (privileges) { }; privileges.categories.userPrivileges = function (cid, uid, callback) { - async.parallel({ - find: async.apply(groups.isMember, uid, 'cid:' + cid + ':privileges:find'), - read: function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:read', next); - }, - 'topics:create': function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:topics:create', next); - }, - 'topics:read': function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:topics:read', next); - }, - 'topics:reply': function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:topics:reply', next); - }, - 'posts:edit': function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:posts:edit', next); - }, - 'posts:delete': function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:posts:delete', next); - }, - 'topics:delete': function (next) { - groups.isMember(uid, 'cid:' + cid + ':privileges:topics:delete', next); - }, - mods: function (next) { - user.isModerator(uid, cid, next); - }, - }, callback); + var tasks = {}; + + privileges.userPrivilegeList.forEach(function (privilege) { + tasks[privilege] = async.apply(groups.isMember, uid, 'cid:' + cid + ':privileges:' + privilege); + }); + + async.parallel(tasks, callback); }; privileges.categories.groupPrivileges = function (cid, groupName, callback) { - async.parallel({ - 'groups:find': async.apply(groups.isMember, groupName, 'cid:' + cid + ':privileges:groups:find'), - 'groups:read': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:read', next); - }, - 'groups:topics:create': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:create', next); - }, - 'groups:topics:reply': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:reply', next); - }, - 'groups:posts:edit': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:posts:edit', next); - }, - 'groups:posts:delete': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:posts:delete', next); - }, - 'groups:topics:delete': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:delete', next); - }, - 'groups:topics:read': function (next) { - groups.isMember(groupName, 'cid:' + cid + ':privileges:groups:topics:read', next); - }, - }, callback); + var tasks = {}; + + privileges.groupPrivilegeList.forEach(function (privilege) { + tasks[privilege] = async.apply(groups.isMember, groupName, 'cid:' + cid + ':privileges:' + privilege); + }); + + async.parallel(tasks, callback); }; }; diff --git a/src/privileges/posts.js b/src/privileges/posts.js index 1855098927..89ef1e0f48 100644 --- a/src/privileges/posts.js +++ b/src/privileges/posts.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('lodash'); var meta = require('../meta'); var posts = require('../posts'); @@ -72,17 +73,18 @@ module.exports = function (privileges) { var tids; var tidToTopic = {}; + pids = _.uniq(pids); + async.waterfall([ function (next) { posts.getPostsFields(pids, ['uid', 'tid', 'deleted'], next); }, function (_posts, next) { postData = _posts; - tids = _posts.map(function (post) { + tids = _.uniq(_posts.map(function (post) { return post && post.tid; - }).filter(function (tid, index, array) { - return tid && array.indexOf(tid) === index; - }); + }).filter(Boolean)); + topics.getTopicsFields(tids, ['deleted', 'cid'], next); }, function (topicData, next) { diff --git a/src/privileges/topics.js b/src/privileges/topics.js index 88273800fe..84e34954e5 100644 --- a/src/privileges/topics.js +++ b/src/privileges/topics.js @@ -16,7 +16,7 @@ module.exports = function (privileges) { privileges.topics.get = function (tid, uid, callback) { var topic; - var privs = ['topics:reply', 'topics:read', 'topics:delete', 'posts:edit', 'posts:delete', 'read']; + var privs = ['topics:reply', 'topics:read', 'topics:tag', 'topics:delete', 'posts:edit', 'posts:delete', 'read']; async.waterfall([ async.apply(topics.getTopicFields, tid, ['cid', 'uid', 'locked', 'deleted']), function (_topic, next) { @@ -41,6 +41,7 @@ module.exports = function (privileges) { plugins.fireHook('filter:privileges.topics.get', { 'topics:reply': (privData['topics:reply'] && !locked && !deleted) || isAdminOrMod, 'topics:read': privData['topics:read'] || isAdminOrMod, + 'topics:tag': privData['topics:tag'] || isAdminOrMod, 'topics:delete': (isOwner && privData['topics:delete']) || isAdminOrMod, 'posts:edit': (privData['posts:edit'] && !locked) || isAdminOrMod, 'posts:delete': (privData['posts:delete'] && !locked) || isAdminOrMod, @@ -81,11 +82,9 @@ module.exports = function (privileges) { }, function (_topicsData, next) { topicsData = _topicsData; - cids = topicsData.map(function (topic) { + cids = _.uniq(topicsData.map(function (topic) { return topic.cid; - }).filter(function (cid, index, array) { - return cid && array.indexOf(cid) === index; - }); + })); privileges.categories.getBase(privilege, cids, uid, next); }, @@ -120,15 +119,14 @@ module.exports = function (privileges) { return callback(null, []); } - uids = uids.filter(function (uid, index, array) { - return array.indexOf(uid) === index; - }); - + uids = _.uniq(uids); + var topicData; async.waterfall([ function (next) { topics.getTopicFields(tid, ['tid', 'cid', 'deleted'], next); }, - function (topicData, next) { + function (_topicData, next) { + topicData = _topicData; async.parallel({ disabled: function (next) { categories.getCategoryField(topicData.cid, 'disabled', next); @@ -142,18 +140,15 @@ module.exports = function (privileges) { isAdmins: function (next) { user.isAdministrator(uids, next); }, - }, function (err, results) { - if (err) { - return next(err); - } - - uids = uids.filter(function (uid, index) { - return parseInt(results.disabled, 10) !== 1 && - ((results.allowedTo[index] && parseInt(topicData.deleted, 10) !== 1) || results.isAdmins[index] || results.isModerators[index]); - }); - - next(null, uids); + }, next); + }, + function (results, next) { + uids = uids.filter(function (uid, index) { + return parseInt(results.disabled, 10) !== 1 && + ((results.allowedTo[index] && parseInt(topicData.deleted, 10) !== 1) || results.isAdmins[index] || results.isModerators[index]); }); + + next(null, uids); }, ], callback); }; diff --git a/src/privileges/users.js b/src/privileges/users.js index 4b04b3ccd2..5e48750dc9 100644 --- a/src/privileges/users.js +++ b/src/privileges/users.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('lodash'); var user = require('../user'); var groups = require('../groups'); @@ -51,9 +52,7 @@ module.exports = function (privileges) { return filterIsModerator(cids, uid, cids.map(function () { return true; }), callback); } - uniqueCids = cids.filter(function (cid, index, array) { - return array.indexOf(cid) === index; - }); + uniqueCids = _.uniq(cids); helpers.isUserAllowedTo('moderate', uid, uniqueCids, next); }, diff --git a/src/reset.js b/src/reset.js index c3bac3bbbb..4b45821ddf 100644 --- a/src/reset.js +++ b/src/reset.js @@ -1,10 +1,12 @@ 'use strict'; +require('colors'); var path = require('path'); var winston = require('winston'); var nconf = require('nconf'); var async = require('async'); var db = require('./database'); +var events = require('./events'); var Reset = {}; @@ -66,6 +68,7 @@ Reset.reset = function (callback) { process.stdout.write('\nPlugin and theme reset flags (-p & -t) can take a single argument\n'); process.stdout.write(' e.g. ./nodebb reset -p nodebb-plugin-mentions, ./nodebb reset -t nodebb-theme-persona\n'); + process.stdout.write(' Prefix is optional, e.g. ./nodebb reset -p markdown, ./nodebb reset -t persona\n'); process.exit(0); } @@ -131,6 +134,12 @@ function resetPlugin(pluginId, callback) { next(); } }, + function (next) { + events.log({ + type: 'plugin-deactivate', + text: pluginId, + }, next); + }, ], function (err) { if (err) { winston.error('[reset] Could not disable plugin: %s encountered error %s', pluginId, err.message); diff --git a/src/routes/debug.js b/src/routes/debug.js index 2ec3f23934..460534fcdc 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -2,77 +2,10 @@ var express = require('express'); var nconf = require('nconf'); -var winston = require('winston'); -var user = require('../user'); -var categories = require('../categories'); -var topics = require('../topics'); -var posts = require('../posts'); module.exports = function (app) { var router = express.Router(); - router.get('/uid/:uid', function (req, res) { - if (!req.params.uid) { - return res.redirect('/404'); - } - - user.getUserData(req.params.uid, function (err, data) { - if (err) { - winston.error(err); - } - - if (data) { - res.send(data); - } else { - res.status(404).json({ - error: "User doesn't exist!", - }); - } - }); - }); - - router.get('/cid/:cid', function (req, res) { - categories.getCategoryData(req.params.cid, function (err, data) { - if (err) { - winston.error(err); - } - - if (data) { - res.send(data); - } else { - res.status(404).send("Category doesn't exist!"); - } - }); - }); - - router.get('/tid/:tid', function (req, res) { - topics.getTopicData(req.params.tid, function (err, data) { - if (err) { - winston.error(err); - } - - if (data) { - res.send(data); - } else { - res.status(404).send("Topic doesn't exist!"); - } - }); - }); - - router.get('/pid/:pid', function (req, res) { - posts.getPostData(req.params.pid, function (err, data) { - if (err) { - winston.error(err); - } - - if (data) { - res.send(data); - } else { - res.status(404).send("Post doesn't exist!"); - } - }); - }); - router.get('/test', function (req, res) { res.redirect(404); }); diff --git a/src/routes/feeds.js b/src/routes/feeds.js index e1fd92cd59..4cfa52c373 100644 --- a/src/routes/feeds.js +++ b/src/routes/feeds.js @@ -12,6 +12,7 @@ var categories = require('../categories'); var meta = require('../meta'); var helpers = require('../controllers/helpers'); var privileges = require('../privileges'); +var db = require('../database'); var controllers404 = require('../controllers/404.js'); module.exports = function (app, middleware) { @@ -26,6 +27,45 @@ module.exports = function (app, middleware) { app.get('/tags/:tag.rss', middleware.maintenanceMode, generateForTag); }; +function validateTokenIfRequiresLogin(requiresLogin, cid, req, res, callback) { + var uid = req.query.uid; + var token = req.query.token; + + if (!requiresLogin) { + return callback(); + } + + if (!uid || !token) { + return helpers.notAllowed(req, res); + } + + async.waterfall([ + function (next) { + db.getObjectField('user:' + uid, 'rss_token', next); + }, + function (_token, next) { + if (token === _token) { + async.waterfall([ + function (next) { + privileges.categories.get(cid, uid, next); + }, + function (privileges, next) { + if (!privileges.read) { + return helpers.notAllowed(req, res); + } + next(); + }, + ], callback); + return; + } + user.auth.logAttempt(uid, req.ip, next); + }, + function () { + helpers.notAllowed(req, res); + }, + ], callback); +} + function generateForTopic(req, res, callback) { if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) { return controllers404.send404(req, res); @@ -33,6 +73,7 @@ function generateForTopic(req, res, callback) { var tid = req.params.topic_id; var userPrivileges; + var topic; async.waterfall([ function (next) { async.parallel({ @@ -48,11 +89,12 @@ function generateForTopic(req, res, callback) { if (!results.topic || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) { return controllers404.send404(req, res); } - if (!results.privileges['topics:read']) { - return helpers.notAllowed(req, res); - } userPrivileges = results.privileges; - topics.getTopicWithPosts(results.topic, 'tid:' + tid + ':posts', req.uid, 0, 25, false, next); + topic = results.topic; + validateTokenIfRequiresLogin(!results.privileges['topics:read'], results.topic.cid, req, res, next); + }, + function (next) { + topics.getTopicWithPosts(topic, 'tid:' + tid + ':posts', req.uid || req.query.uid || 0, 0, 25, false, next); }, function (topicData) { topics.modifyPostsByPrivilege(topicData, userPrivileges); @@ -95,40 +137,12 @@ function generateForTopic(req, res, callback) { ], callback); } -function generateForUserTopics(req, res, callback) { - if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) { - return controllers404.send404(req, res); - } - - var userslug = req.params.userslug; - - async.waterfall([ - function (next) { - user.getUidByUserslug(userslug, next); - }, - function (uid, next) { - if (!uid) { - return callback(); - } - user.getUserFields(uid, ['uid', 'username'], next); - }, - function (userData, next) { - generateForTopics({ - uid: req.uid, - title: 'Topics by ' + userData.username, - description: 'A list of topics that are posted by ' + userData.username, - feed_url: '/user/' + userslug + '/topics.rss', - site_url: '/user/' + userslug + '/topics', - }, 'uid:' + userData.uid + ':topics', req, res, next); - }, - ], callback); -} - function generateForCategory(req, res, next) { if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) { return controllers404.send404(req, res); } var cid = req.params.category_id; + var category; async.waterfall([ function (next) { @@ -143,22 +157,23 @@ function generateForCategory(req, res, next) { reverse: true, start: 0, stop: 25, - uid: req.uid, + uid: req.uid || req.query.uid || 0, }, next); }, }, next); }, function (results, next) { - if (!results.privileges.read) { - return helpers.notAllowed(req, res); - } + category = results.category; + validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, next); + }, + function (next) { generateTopicsFeed({ - uid: req.uid, - title: results.category.name, - description: results.category.description, + uid: req.uid || req.query.uid || 0, + title: category.name, + description: category.description, feed_url: '/category/' + cid + '.rss', - site_url: '/category/' + results.category.cid, - }, results.category.topics, next); + site_url: '/category/' + category.cid, + }, category.topics, next); }, function (feed) { sendFeed(feed, res); @@ -249,7 +264,8 @@ function generateTopicsFeed(feedOptions, feedTopics, callback) { if (topicData.teaser && topicData.teaser.user) { feedItem.description = topicData.teaser.content; feedItem.author = topicData.teaser.user.username; - return next(null, feedItem); + feed.item(feedItem); + return next(); } topics.getMainPost(topicData.tid, feedOptions.uid, function (err, mainPost) { @@ -292,12 +308,13 @@ function generateForRecentPosts(req, res, next) { ], next); } -function generateForCategoryRecentPosts(req, res, next) { +function generateForCategoryRecentPosts(req, res, callback) { if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) { return controllers404.send404(req, res); } var cid = req.params.category_id; - + var category; + var posts; async.waterfall([ function (next) { async.parallel({ @@ -308,29 +325,29 @@ function generateForCategoryRecentPosts(req, res, next) { categories.getCategoryData(cid, next); }, posts: function (next) { - categories.getRecentReplies(cid, req.uid, 20, next); + categories.getRecentReplies(cid, req.uid || req.query.uid || 0, 20, next); }, }, next); }, function (results, next) { if (!results.category) { - return next(); + return controllers404.send404(req, res); } - - if (!results.privileges.read) { - return helpers.notAllowed(req, res); - } - + category = results.category; + posts = results.posts; + validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, next); + }, + function () { var feed = generateForPostsFeed({ - title: results.category.name + ' Recent Posts', - description: 'A list of recent posts from ' + results.category.name, + title: category.name + ' Recent Posts', + description: 'A list of recent posts from ' + category.name, feed_url: '/category/' + cid + '/recentposts.rss', site_url: '/category/' + cid + '/recentposts', - }, results.posts); + }, posts); sendFeed(feed, res); }, - ], next); + ], callback); } function generateForPostsFeed(feedOptions, posts) { @@ -357,6 +374,35 @@ function generateForPostsFeed(feedOptions, posts) { return feed; } +function generateForUserTopics(req, res, callback) { + if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) { + return controllers404.send404(req, res); + } + + var userslug = req.params.userslug; + + async.waterfall([ + function (next) { + user.getUidByUserslug(userslug, next); + }, + function (uid, next) { + if (!uid) { + return callback(); + } + user.getUserFields(uid, ['uid', 'username'], next); + }, + function (userData, next) { + generateForTopics({ + uid: req.uid, + title: 'Topics by ' + userData.username, + description: 'A list of topics that are posted by ' + userData.username, + feed_url: '/user/' + userslug + '/topics.rss', + site_url: '/user/' + userslug + '/topics', + }, 'uid:' + userData.uid + ':topics', req, res, next); + }, + ], callback); +} + function generateForTag(req, res, next) { if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) { return controllers404.send404(req, res); @@ -381,4 +427,3 @@ function sendFeed(feed, res) { var xml = feed.xml(); res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml); } - diff --git a/src/routes/index.js b/src/routes/index.js index 15339b4e11..5023dcc3d3 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -181,7 +181,7 @@ module.exports = function (app, middleware, hotswapIds, callback) { ]; app.use(relativePath, function (req, res, next) { if (deprecatedPaths.some(function (path) { return req.path.startsWith(path); })) { - winston.warn('[deprecated] Accessing `' + req.path.slice(1) + '` from `/` is deprecated. ' + + winston.verbose('[deprecated] Accessing `' + req.path.slice(1) + '` from `/` is deprecated. ' + 'Use `/assets' + req.path + '` to access this file.'); res.redirect(relativePath + '/assets' + req.path + '?' + meta.config['cache-buster']); } else { @@ -190,7 +190,7 @@ module.exports = function (app, middleware, hotswapIds, callback) { }); // DEPRECATED app.use(relativePath + '/api/language', function (req, res) { - winston.warn('[deprecated] Accessing language files from `/api/language` is deprecated. ' + + winston.verbose('[deprecated] Accessing language files from `/api/language` is deprecated. ' + 'Use `/assets/language' + req.path + '.json` for prefetch paths.'); res.redirect(relativePath + '/assets/language' + req.path + '.json?' + meta.config['cache-buster']); }); diff --git a/src/search.js b/src/search.js index 3ed7f4ef35..58b93c8a5a 100644 --- a/src/search.js +++ b/src/search.js @@ -1,7 +1,7 @@ 'use strict'; var async = require('async'); -var validator = require('validator'); +var _ = require('lodash'); var db = require('./database'); var posts = require('./posts'); @@ -31,7 +31,6 @@ search.search = function (data, callback) { } }, function (result, next) { - result.search_query = validator.escape(String(data.query || '')); result.time = (process.elapsedTimeSince(start) / 1000).toFixed(2); next(null, result); }, @@ -81,9 +80,7 @@ function searchInContent(data, callback) { function (mainPids, next) { pids = mainPids.concat(pids).map(function (pid) { return pid && pid.toString(); - }).filter(function (pid, index, array) { - return pid && array.indexOf(pid) === index; - }); + }).filter(Boolean); privileges.posts.filter('read', pids, data.uid, next); }, @@ -394,9 +391,8 @@ function getSearchCids(data, callback) { }, next); }, function (results, next) { - var cids = results.watchedCids.concat(results.childrenCids).concat(data.categories).filter(function (cid, index, array) { - return cid && array.indexOf(cid) === index; - }); + var cids = results.watchedCids.concat(results.childrenCids).concat(data.categories).filter(Boolean); + cids = _.uniq(cids); next(null, cids); }, ], callback); diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 10167d06ff..df169aba0e 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -8,6 +8,8 @@ var meta = require('../meta'); var plugins = require('../plugins'); var widgets = require('../widgets'); var user = require('../user'); +var userDigest = require('../user/digest'); +var userEmail = require('../user/email'); var logger = require('../logger'); var events = require('../events'); var emailer = require('../emailer'); @@ -16,6 +18,7 @@ var analytics = require('../analytics'); var websockets = require('../socket.io/index'); var index = require('./index'); var getAdminSearchDict = require('../admin/search').getDictionary; +var utils = require('../../public/src/utils'); var SocketAdmin = { user: require('./admin/user'), @@ -109,6 +112,10 @@ SocketAdmin.themes.set = function (socket, data, callback) { } }, function (next) { + // Add uid and ip data + data.ip = socket.ip; + data.uid = socket.uid; + meta.themes.set(data, next); }, ], callback); @@ -180,8 +187,10 @@ SocketAdmin.config.setMultiple = function (socket, data, callback) { logger.monitorConfig({ io: index.server }, setting); } } - plugins.fireHook('action:config.set', { settings: data }); - setImmediate(next); + data.type = 'config-change'; + data.uid = socket.uid; + data.ip = socket.ip; + events.log(data, next); }, ], callback); }; @@ -195,7 +204,19 @@ SocketAdmin.settings.get = function (socket, data, callback) { }; SocketAdmin.settings.set = function (socket, data, callback) { - meta.settings.set(data.hash, data.values, callback); + async.waterfall([ + function (next) { + meta.settings.set(data.hash, data.values, next); + }, + function (next) { + var eventData = data.values; + eventData.type = 'settings-change'; + eventData.uid = socket.uid; + eventData.ip = socket.ip; + eventData.hash = data.hash; + events.log(eventData, next); + }, + ], callback); }; SocketAdmin.settings.clearSitemapCache = function (socket, data, callback) { @@ -205,11 +226,39 @@ SocketAdmin.settings.clearSitemapCache = function (socket, data, callback) { SocketAdmin.email.test = function (socket, data, callback) { var site_title = meta.config.title || 'NodeBB'; - emailer.send(data.template, socket.uid, { + var payload = { subject: '[' + site_title + '] Test Email', site_title: site_title, url: nconf.get('url'), - }, callback); + }; + + switch (data.template) { + case 'digest': + userDigest.execute({ + interval: 'day', + subscribers: [socket.uid], + }, callback); + break; + + case 'banned': + Object.assign(payload, { + username: 'test-user', + until: utils.toISOString(Date.now()), + reason: 'Test Reason', + }); + emailer.send(data.template, socket.uid, payload, callback); + break; + + case 'welcome': + userEmail.sendValidationEmail(socket.uid, { + force: 1, + }); + break; + + default: + emailer.send(data.template, socket.uid, payload, callback); + break; + } }; SocketAdmin.analytics.get = function (socket, data, callback) { diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index de2ccfc299..3fb4dfb9ff 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -101,20 +101,9 @@ User.sendValidationEmail = function (socket, uids, callback) { return callback(new Error('[[error:email-confirmations-are-disabled]]')); } - async.waterfall([ - function (next) { - user.getUsersFields(uids, ['uid', 'email'], next); - }, - function (usersData, next) { - async.eachLimit(usersData, 50, function (userData, next) { - if (userData.email && userData.uid) { - user.email.sendValidationEmail(userData.uid, userData.email, next); - } else { - next(); - } - }, next); - }, - ], callback); + async.eachLimit(uids, 50, function (uid, next) { + user.email.sendValidationEmail(uid, next); + }, callback); }; User.sendPasswordResetEmail = function (socket, uids, callback) { diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index 3404fb3fee..db47d0e865 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -160,12 +160,12 @@ SocketCategories.getMoveCategories = function (socket, data, callback) { function (next) { db.getSortedSetRange('cid:0:children', 0, -1, next); }, - function (cids, next) { - privileges.categories.filterCids('read', cids, socket.uid, next); - }, function (cids, next) { categories.getCategories(cids, socket.uid, next); }, + function (categoriesData, next) { + categories.buildForSelectCategories(categoriesData, next); + }, ], next); }, }, next); diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 00d3433a3d..2e897d0c4c 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -93,8 +93,7 @@ function onMessage(socket, payload) { var eventName = payload.data[0]; var params = payload.data[1]; - var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () { - }; + var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () {}; if (!eventName) { return winston.warn('[socket.io] Empty method name'); diff --git a/src/socket.io/notifications.js b/src/socket.io/notifications.js index 80f2bb4e12..3a56ff8a55 100644 --- a/src/socket.io/notifications.js +++ b/src/socket.io/notifications.js @@ -1,5 +1,7 @@ 'use strict'; +var async = require('async'); + var user = require('../user'); var notifications = require('../notifications'); var SocketNotifs = module.exports; @@ -25,13 +27,37 @@ SocketNotifs.deleteAll = function (socket, data, callback) { }; SocketNotifs.markRead = function (socket, nid, callback) { - notifications.markRead(nid, socket.uid, callback); + async.waterfall([ + function (next) { + notifications.markRead(nid, socket.uid, next); + }, + function (next) { + user.notifications.pushCount(socket.uid); + next(); + }, + ], callback); }; SocketNotifs.markUnread = function (socket, nid, callback) { - notifications.markUnread(nid, socket.uid, callback); + async.waterfall([ + function (next) { + notifications.markUnread(nid, socket.uid, next); + }, + function (next) { + user.notifications.pushCount(socket.uid); + next(); + }, + ], callback); }; SocketNotifs.markAllRead = function (socket, data, callback) { - notifications.markAllRead(socket.uid, callback); + async.waterfall([ + function (next) { + notifications.markAllRead(socket.uid, next); + }, + function (next) { + user.notifications.pushCount(socket.uid); + next(); + }, + ], callback); }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index f50c9e53fa..d5b586d627 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -22,7 +22,7 @@ require('./posts/bookmarks')(SocketPosts); require('./posts/tools')(SocketPosts); SocketPosts.reply = function (socket, data, callback) { - if (!data || !data.tid || !data.content) { + if (!data || !data.tid || (parseInt(meta.config.minimumPostLength, 10) !== 0 && !data.content)) { return callback(new Error('[[error:invalid-data]]')); } diff --git a/src/socket.io/posts/edit.js b/src/socket.io/posts/edit.js index 646c51228f..00ae540496 100644 --- a/src/socket.io/posts/edit.js +++ b/src/socket.io/posts/edit.js @@ -15,7 +15,7 @@ module.exports = function (SocketPosts) { SocketPosts.edit = function (socket, data, callback) { if (!socket.uid) { return callback(new Error('[[error:not-logged-in]]')); - } else if (!data || !data.pid || !data.content) { + } else if (!data || !data.pid || (parseInt(meta.config.minimumPostLength, 10) !== 0 && !data.content)) { return callback(new Error('[[error:invalid-data]]')); } @@ -30,7 +30,7 @@ module.exports = function (SocketPosts) { return callback(new Error('[[error:not-enough-tags, ' + meta.config.minimumTagsPerTopic + ']]')); } else if (data.tags && data.tags.length > parseInt(meta.config.maximumTagsPerTopic, 10)) { return callback(new Error('[[error:too-many-tags, ' + meta.config.maximumTagsPerTopic + ']]')); - } else if (contentLen < parseInt(meta.config.minimumPostLength, 10)) { + } else if (parseInt(meta.config.minimumPostLength, 10) !== 0 && contentLen < parseInt(meta.config.minimumPostLength, 10)) { return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); } else if (contentLen > parseInt(meta.config.maximumPostLength, 10)) { return callback(new Error('[[error:content-too-long, ' + meta.config.maximumPostLength + ']]')); diff --git a/src/socket.io/topics/tools.js b/src/socket.io/topics/tools.js index 7302a5ad04..6045353c33 100644 --- a/src/socket.io/topics/tools.js +++ b/src/socket.io/topics/tools.js @@ -86,37 +86,35 @@ module.exports = function (SocketTopics) { } async.each(data.tids, function (tid, next) { + var title; async.waterfall([ function (next) { + topics.getTopicField(tid, 'title', next); + }, + function (_title, next) { + title = _title; topics.tools[action](tid, socket.uid, next); }, function (data, next) { socketHelpers.emitToTopicAndCategory(event, data); - logTopicAction(action, socket, tid, next); + logTopicAction(action, socket, tid, title, next); }, ], next); }, callback); }; - function logTopicAction(action, socket, tid, callback) { + function logTopicAction(action, socket, tid, title, callback) { var actionsToLog = ['delete', 'restore', 'purge']; if (actionsToLog.indexOf(action) === -1) { return setImmediate(callback); } - async.waterfall([ - function (next) { - topics.getTopicField(tid, 'title', next); - }, - function (title, next) { - events.log({ - type: 'topic-' + action, - uid: socket.uid, - ip: socket.ip, - tid: tid, - title: String(title), - }, next); - }, - ], callback); + events.log({ + type: 'topic-' + action, + uid: socket.uid, + ip: socket.ip, + tid: tid, + title: String(title), + }, callback); } SocketTopics.orderPinnedTopics = function (socket, data, callback) { diff --git a/src/socket.io/user.js b/src/socket.io/user.js index f14e0c0c89..d59fad3c58 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -75,18 +75,7 @@ SocketUser.emailConfirm = function (socket, data, callback) { return callback(new Error('[[error:email-confirmations-are-disabled]]')); } - async.waterfall([ - function (next) { - user.getUserField(socket.uid, 'email', next); - }, - function (email, next) { - if (!email) { - return callback(); - } - - user.email.sendValidationEmail(socket.uid, email, next); - }, - ], callback); + user.email.sendValidationEmail(socket.uid, callback); }; diff --git a/src/start.js b/src/start.js index 8d4e465a55..2195a0ec29 100644 --- a/src/start.js +++ b/src/start.js @@ -98,7 +98,7 @@ function setupConfigs() { nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); - nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); + nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); nconf.set('upload_url', '/assets/uploads'); } diff --git a/src/topics.js b/src/topics.js index 575199d400..3952f7b402 100644 --- a/src/topics.js +++ b/src/topics.js @@ -101,14 +101,14 @@ Topics.getTopicsByTids = function (tids, uid, callback) { function mapFilter(array, field) { return array.map(function (topic) { return topic && topic[field] && topic[field].toString(); - }).filter(function (value, index, array) { - return utils.isNumber(value) && array.indexOf(value) === index; + }).filter(function (value) { + return utils.isNumber(value); }); } topics = _topics; - uids = mapFilter(topics, 'uid'); - cids = mapFilter(topics, 'cid'); + uids = _.uniq(mapFilter(topics, 'uid')); + cids = _.uniq(mapFilter(topics, 'cid')); async.parallel({ users: function (next) { diff --git a/src/topics/bookmarks.js b/src/topics/bookmarks.js index b47d5f2278..975a0d54f8 100644 --- a/src/topics/bookmarks.js +++ b/src/topics/bookmarks.js @@ -8,6 +8,9 @@ var posts = require('../posts'); module.exports = function (Topics) { Topics.getUserBookmark = function (tid, uid, callback) { + if (!parseInt(uid, 10)) { + return callback(null, null); + } db.sortedSetScore('tid:' + tid + ':bookmarks', uid, callback); }; diff --git a/src/topics/create.js b/src/topics/create.js index 071093e967..f5a51e7e77 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -104,21 +104,34 @@ module.exports = function (Topics) { if (data.content) { data.content = utils.rtrim(data.content); } + check(data.content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next); }, function (next) { - categories.exists(data.cid, next); + async.parallel({ + categoryExists: function (next) { + categories.exists(data.cid, next); + }, + canCreate: function (next) { + privileges.categories.can('topics:create', data.cid, data.uid, next); + }, + canTag: function (next) { + if (!data.tags.length) { + return next(null, true); + } + privileges.categories.can('topics:tag', data.cid, data.uid, next); + }, + }, next); }, - function (categoryExists, next) { - if (!categoryExists) { + function (results, next) { + if (!results.categoryExists) { return next(new Error('[[error:no-category]]')); } - privileges.categories.can('topics:create', data.cid, data.uid, next); - }, - function (canCreate, next) { - if (!canCreate) { + + if (!results.canCreate || !results.canTag) { return next(new Error('[[error:no-privileges]]')); } + guestHandleValid(data, next); }, function (next) { @@ -333,7 +346,7 @@ module.exports = function (Topics) { item = S(item).stripTags().s.trim(); } - if (!item || item.length < parseInt(min, 10)) { + if (item === null || item === undefined || item.length < parseInt(min, 10)) { return callback(new Error('[[error:' + minError + ', ' + min + ']]')); } else if (item.length > parseInt(max, 10)) { return callback(new Error('[[error:' + maxError + ', ' + max + ']]')); diff --git a/src/topics/posts.js b/src/topics/posts.js index 8377215609..ebfbd106c0 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -169,11 +169,9 @@ module.exports = function (Topics) { async.apply(posts.getPostsFields, parentPids, ['uid']), function (_parentPosts, next) { parentPosts = _parentPosts; - var parentUids = parentPosts.map(function (postObj) { - return parseInt(postObj.uid, 10); - }).filter(function (uid, idx, users) { - return users.indexOf(uid) === idx; - }); + var parentUids = _.uniq(parentPosts.map(function (postObj) { + return postObj && parseInt(postObj.uid, 10); + })); user.getUsersFields(parentUids, ['username'], next); }, @@ -391,59 +389,70 @@ module.exports = function (Topics) { }; function getPostReplies(pids, callerUid, callback) { - async.map(pids, function (pid, next) { - var replyPids; - var uids = []; - async.waterfall([ - function (next) { - db.getSortedSetRange('pid:' + pid + ':replies', 0, -1, next); - }, - function (_replyPids, next) { - replyPids = _replyPids; + var arrayOfReplyPids; + var replyData; + var uniqueUids; + var uniquePids; + async.waterfall([ + function (next) { + var keys = pids.map(function (pid) { + return 'pid:' + pid + ':replies'; + }); + db.getSortedSetsMembers(keys, next); + }, + function (arrayOfPids, next) { + arrayOfReplyPids = arrayOfPids; - var count = 0; + uniquePids = _.uniq(_.flatten(arrayOfPids)); - async.until(function () { - return count === replyPids.length || uids.length === 6; - }, function (next) { - async.waterfall([ - function (next) { - posts.getPostField(replyPids[count], 'uid', next); - }, - function (uid, next) { - uid = parseInt(uid, 10); - if (uids.indexOf(uid) === -1) { - uids.push(uid); - } - count += 1; - next(); - }, - ], next); - }, next); - }, - function (next) { - async.parallel({ - users: function (next) { - user.getUsersWithFields(uids, ['uid', 'username', 'userslug', 'picture'], callerUid, next); - }, - timestampISO: function (next) { - posts.getPostField(replyPids[0], 'timestamp', function (err, timestamp) { - next(err, utils.toISOString(timestamp)); - }); - }, - }, next); - }, - function (replies, next) { - if (replies.users.length > 5) { - replies.users.shift(); - replies.hasMore = true; + posts.getPostsFields(uniquePids, ['pid', 'uid', 'timestamp'], next); + }, + function (_replyData, next) { + replyData = _replyData; + var uids = replyData.map(function (replyData) { + return replyData && replyData.uid; + }); + + uniqueUids = _.uniq(uids); + + user.getUsersWithFields(uniqueUids, ['uid', 'username', 'userslug', 'picture'], callerUid, next); + }, + function (userData, next) { + var uidMap = _.zipObject(uniqueUids, userData); + var pidMap = _.zipObject(uniquePids, replyData); + + var returnData = arrayOfReplyPids.map(function (replyPids) { + var uidsUsed = {}; + var currentData = { + hasMore: false, + users: [], + text: replyPids.length > 1 ? '[[topic:replies_to_this_post, ' + replyPids.length + ']]' : '[[topic:one_reply_to_this_post]]', + count: replyPids.length, + timestampISO: replyPids.length ? utils.toISOString(pidMap[replyPids[0]].timestamp) : undefined, + }; + + replyPids.sort(function (a, b) { + return parseInt(a, 10) - parseInt(b, 10); + }); + + replyPids.forEach(function (replyPid) { + var replyData = pidMap[replyPid]; + if (!uidsUsed[replyData.uid] && currentData.users.length < 6) { + currentData.users.push(uidMap[replyData.uid]); + uidsUsed[replyData.uid] = true; + } + }); + + if (currentData.users.length > 5) { + currentData.users.pop(); + currentData.hasMore = true; } - replies.count = replyPids.length; - replies.text = replies.count > 1 ? '[[topic:replies_to_this_post, ' + replies.count + ']]' : '[[topic:one_reply_to_this_post]]'; - next(null, replies); - }, - ], next); - }, callback); + return currentData; + }); + + next(null, returnData); + }, + ], callback); } }; diff --git a/src/topics/recent.js b/src/topics/recent.js index b2e0de6023..b2a9d28a63 100644 --- a/src/topics/recent.js +++ b/src/topics/recent.js @@ -90,7 +90,7 @@ module.exports = function (Topics) { ], callback); } - + /* not an orphan method, used in widget-essentials */ Topics.getLatestTopics = function (uid, start, stop, term, callback) { async.waterfall([ function (next) { diff --git a/src/topics/suggested.js b/src/topics/suggested.js index 33153a580c..e2b965fd4e 100644 --- a/src/topics/suggested.js +++ b/src/topics/suggested.js @@ -25,8 +25,8 @@ module.exports = function (Topics) { }, function (results, next) { var tids = results.tagTids.concat(results.searchTids).concat(results.categoryTids); - tids = tids.filter(function (_tid, index, array) { - return parseInt(_tid, 10) !== parseInt(tid, 10) && array.indexOf(_tid) === index; + tids = _.uniq(tids).filter(function (_tid) { + return parseInt(_tid, 10) !== parseInt(tid, 10); }); if (stop === -1) { diff --git a/src/topics/tags.js b/src/topics/tags.js index dbfb092cc1..f936d7da4a 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -23,11 +23,12 @@ module.exports = function (Topics) { plugins.fireHook('filter:tags.filter', { tags: tags, tid: tid }, next); }, function (data, next) { - tags = data.tags.slice(0, meta.config.maximumTagsPerTopic || 5); + tags = _.uniq(data.tags); + tags = tags.slice(0, meta.config.maximumTagsPerTopic || 5); tags = tags.map(function (tag) { return utils.cleanUpTag(tag, meta.config.maximumTagLength); - }).filter(function (tag, index, array) { - return tag && tag.length >= (meta.config.minimumTagLength || 3) && array.indexOf(tag) === index; + }).filter(function (tag) { + return tag && tag.length >= (meta.config.minimumTagLength || 3); }); filterCategoryTags(tags, tid, next); diff --git a/src/topics/teaser.js b/src/topics/teaser.js index d62dc2fe72..09d4d048ae 100644 --- a/src/topics/teaser.js +++ b/src/topics/teaser.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('lodash'); var S = require('string'); var winston = require('winston'); @@ -57,11 +58,9 @@ module.exports = function (Topics) { }, function (_postData, next) { postData = _postData; - var uids = postData.map(function (post) { + var uids = _.uniq(postData.map(function (post) { return post.uid; - }).filter(function (uid, index, array) { - return array.indexOf(uid) === index; - }); + })); user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], next); }, diff --git a/src/topics/unread.js b/src/topics/unread.js index 073a680358..4a46e4c0c9 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('lodash'); var db = require('../database'); var user = require('../user'); @@ -118,10 +119,10 @@ module.exports = function (Topics) { } }).map(function (topic) { return topic.value; - }).filter(function (tid, index, array) { - return array.indexOf(tid) === index; }); + tids = _.uniq(tids); + if (params.filter === 'watched') { Topics.filterWatchedTids(tids, uid, next); } else { @@ -222,8 +223,8 @@ module.exports = function (Topics) { return setImmediate(callback, null, false); } - tids = tids.filter(function (tid, index, array) { - return tid && utils.isNumber(tid) && array.indexOf(tid) === index; + tids = _.uniq(tids).filter(function (tid) { + return tid && utils.isNumber(tid); }); if (!tids.length) { @@ -260,9 +261,9 @@ module.exports = function (Topics) { function (results, next) { var cids = results.topicData.map(function (topic) { return topic && topic.cid; - }).filter(function (topic, index, array) { - return topic && array.indexOf(topic) === index; - }); + }).filter(Boolean); + + cids = _.uniq(cids); categories.markAsRead(cids, uid, next); }, diff --git a/src/upgrade.js b/src/upgrade.js index 2bba82dd91..f1f5a016ad 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -129,11 +129,14 @@ Upgrade.process = function (files, skipCount, callback) { date: date, }; - process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '...\n '); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '...\n'); // For backwards compatibility, cross-reference with schemaDate (if found). If a script's date is older, skip it if ((!results.schemaDate && !results.schemaLogCount) || (scriptExport.timestamp <= results.schemaDate && semver.lt(version, '1.5.0'))) { - process.stdout.write('skipped\n'.grey); + readline.clearLine(process.stdout, 0); + readline.cursorTo(process.stdout, 0); + readline.moveCursor(process.stdout, 0, -1); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... ' + 'skipped\n'.grey); db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); return; } @@ -147,13 +150,11 @@ Upgrade.process = function (files, skipCount, callback) { return next(err); } - if (progress.total > 0) { - readline.clearLine(process.stdout, 0); - readline.cursorTo(process.stdout, 0); - process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... '); - } + readline.clearLine(process.stdout, 0); + readline.cursorTo(process.stdout, 0); + readline.moveCursor(process.stdout, 0, -1); + process.stdout.write(' → '.white + String('[' + [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()].join('/') + '] ').gray + String(scriptExport.name).reset + '... ' + 'OK\n'.green); - process.stdout.write('OK\n'.green); // Record success in schemaLog db.sortedSetAdd('schemaLog', Date.now(), path.basename(file, '.js'), next); }); diff --git a/src/upgrades/1.5.1/rename_mods_group.js b/src/upgrades/1.5.1/rename_mods_group.js index 516074eb57..9c46947099 100644 --- a/src/upgrades/1.5.1/rename_mods_group.js +++ b/src/upgrades/1.5.1/rename_mods_group.js @@ -21,7 +21,7 @@ module.exports = { progress.incr(); return next(err); } - winston.info('renaming ' + groupName + ' to ' + newName); + winston.verbose('renaming ' + groupName + ' to ' + newName); progress.incr(); groups.renameGroup(groupName, newName, next); }); diff --git a/src/upgrades/1.5.2/rss_token_wipe.js b/src/upgrades/1.5.2/rss_token_wipe.js new file mode 100644 index 0000000000..2bfa9cd02b --- /dev/null +++ b/src/upgrades/1.5.2/rss_token_wipe.js @@ -0,0 +1,22 @@ +'use strict'; + +var async = require('async'); +var batch = require('../../batch'); +var db = require('../../database'); + +module.exports = { + name: 'Wipe all existing RSS tokens', + timestamp: Date.UTC(2017, 6, 5), + method: function (callback) { + var progress = this.progress; + + batch.processSortedSet('users:joindate', function (uids, next) { + async.eachLimit(uids, 500, function (uid, next) { + progress.incr(); + db.deleteObjectField('user:' + uid, 'rss_token', next); + }, next); + }, { + progress: progress, + }, callback); + }, +}; diff --git a/src/upgrades/1.5.2/tags_privilege.js b/src/upgrades/1.5.2/tags_privilege.js new file mode 100644 index 0000000000..d5d703407c --- /dev/null +++ b/src/upgrades/1.5.2/tags_privilege.js @@ -0,0 +1,22 @@ +'use strict'; + +var async = require('async'); + +var batch = require('../../batch'); + +module.exports = { + name: 'Give tag privilege to registered-users on all categories', + timestamp: Date.UTC(2017, 5, 16), + method: function (callback) { + var progress = this.progress; + var privileges = require('../../privileges'); + batch.processSortedSet('categories:cid', function (cids, next) { + async.eachSeries(cids, function (cid, next) { + progress.incr(); + privileges.categories.give(['topics:tag'], cid, 'registered-users', next); + }, next); + }, { + progress: progress, + }, callback); + }, +}; diff --git a/src/user.js b/src/user.js index e4ad24283c..db265f76bc 100644 --- a/src/user.js +++ b/src/user.js @@ -64,9 +64,7 @@ User.getUsersWithFields = function (uids, fields, uid, callback) { plugins.fireHook('filter:users.addFields', { fields: fields }, next); }, function (data, next) { - data.fields = data.fields.filter(function (field, index, array) { - return array.indexOf(field) === index; - }); + data.fields = _.uniq(data.fields); async.parallel({ userData: function (next) { @@ -80,12 +78,24 @@ User.getUsersWithFields = function (uids, fields, uid, callback) { function (results, next) { results.userData.forEach(function (user, index) { if (user) { - user.status = User.getStatus(user); user.administrator = results.isAdmin[index]; - user.banned = parseInt(user.banned, 10) === 1; - user.banned_until = parseInt(user['banned:expire'], 10) || 0; - user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned'; - user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1; + + if (user.hasOwnProperty('status')) { + user.status = User.getStatus(user); + } + + if (user.hasOwnProperty('banned')) { + user.banned = parseInt(user.banned, 10) === 1; + } + + if (user.hasOwnProperty('banned:expire')) { + user.banned_until = parseInt(user['banned:expire'], 10) || 0; + user.banned_until_readable = user.banned_until ? new Date(user.banned_until).toString() : 'Not Banned'; + } + + if (user.hasOwnProperty(['email:confirmed'])) { + user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1; + } } }); plugins.fireHook('filter:userlist.get', { users: results.userData, uid: uid }, next); @@ -279,12 +289,30 @@ User.getModeratorUids = function (callback) { async.waterfall([ async.apply(db.getSortedSetRange, 'categories:cid', 0, -1), function (cids, next) { - var groupNames = cids.map(function (cid) { - return 'cid:' + cid + ':privileges:moderate'; - }); + var groupNames = cids.reduce(function (memo, cid) { + memo.push('cid:' + cid + ':privileges:moderate'); + memo.push('cid:' + cid + ':privileges:groups:moderate'); + return memo; + }, []); groups.getMembersOfGroups(groupNames, next); }, + function (memberSets, next) { + // Every other set is actually a list of user groups, not uids, so convert those to members + var sets = memberSets.reduce(function (memo, set, idx) { + if (idx % 2) { + memo.working.push(set); + } else { + memo.regular.push(set); + } + + return memo; + }, { working: [], regular: [] }); + + groups.getMembersOfGroups(sets.working, function (err, memberSets) { + next(err, sets.regular.concat(memberSets || [])); + }); + }, function (memberSets, next) { next(null, _.union.apply(_, memberSets)); }, diff --git a/src/user/auth.js b/src/user/auth.js index 0a5c06fcb6..1fbc316f18 100644 --- a/src/user/auth.js +++ b/src/user/auth.js @@ -6,6 +6,7 @@ var db = require('../database'); var meta = require('../meta'); var events = require('../events'); var batch = require('../batch'); +var utils = require('../utils'); module.exports = function (User) { User.auth = {}; @@ -47,6 +48,29 @@ module.exports = function (User) { ], callback); }; + User.auth.getFeedToken = function (uid, callback) { + if (!uid) { + return callback(); + } + var token; + async.waterfall([ + function (next) { + db.getObjectField('user:' + uid, 'rss_token', next); + }, + function (_token, next) { + token = _token || utils.generateUUID(); + if (!_token) { + User.setUserField(uid, 'rss_token', token, next); + } else { + next(); + } + }, + function (next) { + next(null, token); + }, + ], callback); + }; + User.auth.clearLoginAttempts = function (uid) { db.delete('loginAttempts:' + uid); }; diff --git a/src/user/create.js b/src/user/create.js index 812e3c0a4a..8b684cdacb 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -103,7 +103,9 @@ module.exports = function (User) { ], next); if (parseInt(userData.uid, 10) !== 1 && parseInt(meta.config.requireEmailConfirmation, 10) === 1) { - User.email.sendValidationEmail(userData.uid, userData.email); + User.email.sendValidationEmail(userData.uid, { + email: userData.email, + }); } } else { next(); diff --git a/src/user/data.js b/src/user/data.js index f5f4fd3db7..3c79a9095f 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -31,6 +31,10 @@ module.exports = function (User) { return callback(null, []); } + uids = uids.map(function (uid) { + return isNaN(uid) ? 0 : uid; + }); + var fieldsToRemove = []; function addField(field) { if (fields.indexOf(field) === -1) { @@ -39,7 +43,7 @@ module.exports = function (User) { } } - if (fields.indexOf('uid') === -1) { + if (fields.length && fields.indexOf('uid') === -1) { fields.push('uid'); } @@ -58,7 +62,11 @@ module.exports = function (User) { async.waterfall([ function (next) { - db.getObjectsFields(uidsToUserKeys(uniqueUids), fields, next); + if (fields.length) { + db.getObjectsFields(uidsToUserKeys(uniqueUids), fields, next); + } else { + db.getObjects(uidsToUserKeys(uniqueUids), next); + } }, function (users, next) { users = uidsToUsers(uids, uniqueUids, users); @@ -80,24 +88,7 @@ module.exports = function (User) { }; User.getUsersData = function (uids, callback) { - if (!Array.isArray(uids) || !uids.length) { - return callback(null, []); - } - - var uniqueUids = uids.filter(function (uid, index) { - return index === uids.indexOf(uid); - }); - - async.waterfall([ - function (next) { - db.getObjects(uidsToUserKeys(uniqueUids), next); - }, - function (users, next) { - users = uidsToUsers(uids, uniqueUids, users); - - modifyUserData(users, [], next); - }, - ], callback); + User.getUsersFields(uids, [], callback); }; function uidsToUsers(uids, uniqueUids, usersData) { @@ -131,6 +122,10 @@ module.exports = function (User) { user.password = undefined; } + if (user.rss_token) { + user.rss_token = undefined; + } + if (!parseInt(user.uid, 10)) { user.uid = 0; user.username = '[[global:guest]]'; diff --git a/src/user/delete.js b/src/user/delete.js index 7392192c8b..92fd8f27b9 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -1,6 +1,7 @@ 'use strict'; var async = require('async'); +var _ = require('lodash'); var db = require('../database'); var posts = require('../posts'); @@ -152,9 +153,7 @@ module.exports = function (User) { }, next); }, function (pids, next) { - pids = pids.upvotedPids.concat(pids.downvotedPids).filter(function (pid, index, array) { - return pid && array.indexOf(pid) === index; - }); + pids = _.uniq(pids.upvotedPids.concat(pids.downvotedPids).filter(Boolean)); async.eachSeries(pids, function (pid, next) { posts.unvote(pid, uid, next); diff --git a/src/user/digest.js b/src/user/digest.js index 6381a8e262..61b727de3a 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -4,7 +4,7 @@ var async = require('async'); var winston = require('winston'); var nconf = require('nconf'); -var db = require('../database'); +var batch = require('../batch'); var meta = require('../meta'); var user = require('../user'); var topics = require('../topics'); @@ -14,47 +14,40 @@ var utils = require('../utils'); var Digest = module.exports; -Digest.execute = function (interval, callback) { +Digest.execute = function (payload, callback) { callback = callback || function () {}; var digestsDisabled = parseInt(meta.config.disableEmailSubscriptions, 10) === 1; if (digestsDisabled) { - winston.info('[user/jobs] Did not send digests (' + interval + ') because subscription system is disabled.'); + winston.info('[user/jobs] Did not send digests (' + payload.interval + ') because subscription system is disabled.'); return callback(); } - var subscribers; async.waterfall([ function (next) { - async.parallel({ - topics: async.apply(topics.getLatestTopics, 0, 0, 9, interval), - subscribers: async.apply(Digest.getSubscribers, interval), - }, next); + if (payload.subscribers) { + setImmediate(next, undefined, payload.subscribers); + } else { + Digest.getSubscribers(payload.interval, next); + } }, - function (data, next) { - subscribers = data.subscribers; - if (!data.subscribers.length) { + function (subscribers, next) { + if (!subscribers.length) { return callback(); } - // Fix relative paths in topic data - data.topics.topics = data.topics.topics.map(function (topicObj) { - var user = topicObj.hasOwnProperty('teaser') && topicObj.teaser !== undefined ? topicObj.teaser.user : topicObj.user; - if (user && user.picture && utils.isRelativeUrl(user.picture)) { - user.picture = nconf.get('base_url') + user.picture; - } + var data = { + interval: payload.interval, + subscribers: subscribers, + }; - return topicObj; - }); - - data.interval = interval; Digest.send(data, next); }, - ], function (err) { + ], function (err, count) { if (err) { - winston.error('[user/jobs] Could not send digests (' + interval + '): ' + err.message); + winston.error('[user/jobs] Could not send digests (' + payload.interval + '): ' + err.message); } else { - winston.info('[user/jobs] Digest (' + interval + ') scheduling completed. ' + subscribers.length + ' email(s) sent.'); + winston.info('[user/jobs] Digest (' + payload.interval + ') scheduling completed. ' + count + ' email(s) sent.'); } callback(err); @@ -64,7 +57,25 @@ Digest.execute = function (interval, callback) { Digest.getSubscribers = function (interval, callback) { async.waterfall([ function (next) { - db.getSortedSetRange('digest:' + interval + ':uids', 0, -1, next); + var subs = []; + + batch.processSortedSet('users:joindate', function (uids, next) { + async.waterfall([ + function (next) { + user.getMultipleUserSettings(uids, next); + }, + function (settings, next) { + settings.forEach(function (hash) { + if (hash.dailyDigestFreq === interval) { + subs.push(hash.uid); + } + }); + next(); + }, + ], next); + }, { interval: 1000 }, function (err) { + next(err, subs); + }); }, function (subscribers, next) { plugins.fireHook('filter:digest.subscribers', { @@ -92,12 +103,16 @@ Digest.send = function (data, callback) { async.eachLimit(users, 100, function (userObj, next) { async.waterfall([ function (next) { - user.notifications.getDailyUnread(userObj.uid, next); + async.parallel({ + notifications: async.apply(user.notifications.getDailyUnread, userObj.uid), + topics: async.apply(topics.getPopular, data.interval, userObj.uid, 10), + }, next); }, - function (notifications, next) { - notifications = notifications.filter(Boolean); + function (data, next) { + var notifications = data.notifications.filter(Boolean); + // If there are no notifications and no new topics, don't bother sending a digest - if (!notifications.length && !data.topics.topics.length) { + if (!notifications.length && !data.topics.length) { return next(); } @@ -107,6 +122,16 @@ Digest.send = function (data, callback) { } }); + // Fix relative paths in topic data + data.topics = data.topics.map(function (topicObj) { + var user = topicObj.hasOwnProperty('teaser') && topicObj.teaser !== undefined ? topicObj.teaser.user : topicObj.user; + if (user && user.picture && utils.isRelativeUrl(user.picture)) { + user.picture = nconf.get('base_url') + user.picture; + } + + return topicObj; + }); + emailer.send('digest', userObj.uid, { subject: '[' + meta.config.title + '] [[email:digest.subject, ' + (now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate()) + ']]', username: userObj.username, @@ -114,7 +139,7 @@ Digest.send = function (data, callback) { url: nconf.get('url'), site_title: meta.config.title || meta.config.browserTitle || 'NodeBB', notifications: notifications, - recent: data.topics.topics, + recent: data.topics, interval: data.interval, }); next(); @@ -123,6 +148,6 @@ Digest.send = function (data, callback) { }, next); }, ], function (err) { - callback(err); + callback(err, data.subscribers.length); }); }; diff --git a/src/user/email.js b/src/user/email.js index 30d8340e8a..734e247eb1 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -26,7 +26,26 @@ UserEmail.available = function (email, callback) { }); }; -UserEmail.sendValidationEmail = function (uid, email, callback) { +UserEmail.sendValidationEmail = function (uid, options, callback) { + /* + * Options: + * - email, overrides email retrieval + * - force, sends email even if it is too soon to send another + */ + + // Handling for 2 arguments + if (arguments.length === 2 && typeof options === 'function') { + callback = options; + options = {}; + } + + // Fallback behaviour (email passed in as second argument) + if (typeof options === 'string') { + options = { + email: options, + }; + } + callback = callback || function () {}; var confirm_code = utils.generateUUID(); var confirm_link = nconf.get('url') + '/confirm/' + confirm_code; @@ -35,6 +54,23 @@ UserEmail.sendValidationEmail = function (uid, email, callback) { async.waterfall([ function (next) { + // If no email passed in (default), retrieve email from uid + if (options.email && options.email.length) { + return setImmediate(next, null, options.email); + } + + user.getUserField(uid, 'email', next); + }, + function (email, next) { + options.email = email; + if (!options.email) { + return callback(); + } + + if (options.force) { + return setImmediate(next, null, false); + } + db.get('uid:' + uid + ':confirm:email:sent', next); }, function (sent, next) { @@ -52,7 +88,7 @@ UserEmail.sendValidationEmail = function (uid, email, callback) { function (_confirm_code, next) { confirm_code = _confirm_code; db.setObject('confirm:' + confirm_code, { - email: email.toLowerCase(), + email: options.email.toLowerCase(), uid: uid, }, next); }, diff --git a/src/user/jobs.js b/src/user/jobs.js index 7950e3fdcb..b519c22a9c 100644 --- a/src/user/jobs.js +++ b/src/user/jobs.js @@ -42,7 +42,7 @@ module.exports = function (User) { function startDigestJob(name, cronString, term) { jobs[name] = new cronJob(cronString, function () { winston.verbose('[user/jobs] Digest job (' + name + ') started.'); - User.digest.execute(term); + User.digest.execute({ interval: term }); }, null, true); winston.verbose('[user/jobs] Starting job (' + name + ')'); } diff --git a/src/user/profile.js b/src/user/profile.js index 91956e9bf4..d3067b6945 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -176,7 +176,9 @@ module.exports = function (User) { }, function (next) { if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && newEmail) { - User.email.sendValidationEmail(uid, newEmail); + User.email.sendValidationEmail(uid, { + email: newEmail, + }); } User.setUserField(uid, 'email:confirmed', 0, next); }, @@ -281,7 +283,10 @@ module.exports = function (User) { }, function (hashedPassword, next) { async.parallel([ - async.apply(User.setUserField, data.uid, 'password', hashedPassword), + async.apply(User.setUserFields, data.uid, { + password: hashedPassword, + rss_token: utils.generateUUID(), + }), async.apply(User.reset.updateExpiry, data.uid), async.apply(User.auth.revokeAllSessions, data.uid), ], function (err) { diff --git a/src/views/admin/manage/category.tpl b/src/views/admin/manage/category.tpl index c8be81ab8f..7c39ddee44 100644 --- a/src/views/admin/manage/category.tpl +++ b/src/views/admin/manage/category.tpl @@ -1,19 +1,26 @@
    - + +
    - -
    +
    @@ -83,6 +90,16 @@
    +
    +
    +
    + +
    +
    +

    diff --git a/src/views/admin/manage/groups.tpl b/src/views/admin/manage/groups.tpl index 44c4187604..72f8bdf35e 100644 --- a/src/views/admin/manage/groups.tpl +++ b/src/views/admin/manage/groups.tpl @@ -57,11 +57,11 @@
    - +
    - +
    diff --git a/src/views/admin/partials/categories/category-rows.tpl b/src/views/admin/partials/categories/category-rows.tpl index 1ceeefb9d4..c4bfd008a5 100644 --- a/src/views/admin/partials/categories/category-rows.tpl +++ b/src/views/admin/partials/categories/category-rows.tpl @@ -4,8 +4,11 @@
    +
    + +
    {categories.name}
    -

    {categories.descriptionParsed}

    - - + - + [[admin/manage/categories:edit]]
    diff --git a/src/views/admin/partials/categories/select-category.tpl b/src/views/admin/partials/categories/select-category.tpl index 8c93286507..1cf5e90529 100644 --- a/src/views/admin/partials/categories/select-category.tpl +++ b/src/views/admin/partials/categories/select-category.tpl @@ -1,10 +1,16 @@
    - - +
    + + +
    \ No newline at end of file diff --git a/src/views/admin/partials/categories/setParent.tpl b/src/views/admin/partials/categories/setParent.tpl deleted file mode 100644 index e8e6b9f860..0000000000 --- a/src/views/admin/partials/categories/setParent.tpl +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/src/webserver.js b/src/webserver.js index 2d26045b8b..a7dc0a8182 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -24,6 +24,7 @@ var meta = require('./meta'); var languages = require('./languages'); var logger = require('./logger'); var plugins = require('./plugins'); +var flags = require('./flags'); var routes = require('./routes'); var auth = require('./routes/authentication'); var templates = require('templates.js'); @@ -106,6 +107,7 @@ function initializeNodeBB(callback) { meta.sounds.addUploads, languages.init, meta.blacklist.load, + flags.init, ], next); }, ], function (err) { @@ -133,8 +135,8 @@ function setupExpressApp(app, callback) { app.use(compression()); - app.get('/ping', ping); - app.get('/sping', ping); + app.get(relativePath + '/ping', ping); + app.get(relativePath + '/sping', ping); setupFavicon(app); diff --git a/test/categories.js b/test/categories.js index 9c481fc317..53a867e858 100644 --- a/test/categories.js +++ b/test/categories.js @@ -640,14 +640,18 @@ describe('Categories', function () { assert.ifError(err); assert.deepEqual(data, { find: false, - mods: false, 'posts:delete': false, read: false, 'topics:reply': false, 'topics:read': false, 'topics:create': false, + 'topics:tag': false, 'topics:delete': false, 'posts:edit': false, + 'upload:post:file': false, + 'upload:post:image': false, + purge: false, + moderate: false, }); done(); @@ -663,9 +667,14 @@ describe('Categories', function () { 'groups:topics:delete': false, 'groups:topics:create': true, 'groups:topics:reply': true, + 'groups:topics:tag': true, 'groups:posts:delete': true, 'groups:read': true, 'groups:topics:read': true, + 'groups:upload:post:file': false, + 'groups:upload:post:image': true, + 'groups:purge': false, + 'groups:moderate': false, }); done(); diff --git a/test/controllers.js b/test/controllers.js index 8b01551773..34c515fc7a 100644 --- a/test/controllers.js +++ b/test/controllers.js @@ -596,7 +596,7 @@ describe('Controllers', function () { user.create({ username: 'revokeme', password: 'barbar' }, function (err, _uid) { assert.ifError(err); uid = _uid; - helpers.loginUser('revokeme', 'barbar', function (err, _jar, io, _csrf_token) { + helpers.loginUser('revokeme', 'barbar', function (err, _jar, _csrf_token) { assert.ifError(err); jar = _jar; csrf_token = _csrf_token; @@ -808,7 +808,8 @@ describe('Controllers', function () { it('should redirect to account page with logged in user', function (done) { request(nconf.get('url') + '/api/login', { jar: jar, json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(res.statusCode, 308); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/user/foo'); assert.equal(body, '/user/foo'); done(); }); @@ -825,7 +826,8 @@ describe('Controllers', function () { it('should redirect to userslug', function (done) { request(nconf.get('url') + '/api/uid/' + fooUid, { json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(res.statusCode, 308); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/user/foo'); assert.equal(body, '/user/foo'); done(); }); @@ -1238,10 +1240,11 @@ describe('Controllers', function () { }); it('should return correct post path', function (done) { - request(nconf.get('url') + '/api/post/' + pid, function (err, res, body) { + request(nconf.get('url') + '/api/post/' + pid, { json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(res.statusCode, 308); - assert.equal(body, '"/topic/1/test-topic-title/1"'); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/topic/1/test-topic-title/1'); + assert.equal(body, '/topic/1/test-topic-title/1'); done(); }); }); @@ -1411,7 +1414,8 @@ describe('Controllers', function () { request(nconf.get('url') + '/api/users', { json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(res.statusCode, 308); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/api/popular'); assert(body, '/api/popular'); done(); }); @@ -1521,7 +1525,8 @@ describe('Controllers', function () { it('should redirect if topic index is negative', function (done) { request(nconf.get('url') + '/api/category/' + category.slug + '/-10', function (err, res) { assert.ifError(err); - assert.equal(res.statusCode, 308); + assert.equal(res.statusCode, 200); + assert.ok(res.headers['x-redirect']); done(); }); }); @@ -1627,7 +1632,8 @@ describe('Controllers', function () { function (category, next) { request(nconf.get('url') + '/api/category/' + category.slug, { jar: jar, json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(res.statusCode, 308); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], 'https://nodebb.org'); assert.equal(body, 'https://nodebb.org'); next(); }); @@ -1754,10 +1760,11 @@ describe('Controllers', function () { }); it('should redirect if page is out of bounds', function (done) { - request(nconf.get('url') + '/api/unread?page=-1', { jar: jar }, function (err, res, body) { + request(nconf.get('url') + '/api/unread?page=-1', { jar: jar, json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(res.statusCode, 308); - assert.equal(body, '"/unread?page=1"'); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/unread?page=1'); + assert.equal(body, '/unread?page=1'); done(); }); }); diff --git a/test/database/hash.js b/test/database/hash.js index c1b12aaed7..7b128d76f1 100644 --- a/test/database/hash.js +++ b/test/database/hash.js @@ -194,8 +194,11 @@ describe('Hash methods', function () { it('should return undefined for all fields if object does not exist', function (done) { db.getObjectsFields(['doesnotexist1', 'doesnotexist2'], ['name', 'age'], function (err, data) { assert.ifError(err); - assert.equal(data.name, null); - assert.equal(data.age, null); + assert(Array.isArray(data)); + assert.equal(data[0].name, null); + assert.equal(data[0].age, null); + assert.equal(data[1].name, null); + assert.equal(data[1].age, null); done(); }); }); diff --git a/test/feeds.js b/test/feeds.js index e8e3f8dfa4..4ed9fe0937 100644 --- a/test/feeds.js +++ b/test/feeds.js @@ -12,6 +12,7 @@ var groups = require('../src/groups'); var user = require('../src/user'); var meta = require('../src/meta'); var privileges = require('../src/privileges'); +var helpers = require('./helpers'); describe('feeds', function () { var tid; @@ -113,4 +114,81 @@ describe('feeds', function () { }); }); }); + + describe('private feeds and tokens', function () { + var jar; + var rssToken; + before(function (done) { + helpers.loginUser('foo', 'barbar', function (err, _jar) { + assert.ifError(err); + jar = _jar; + done(); + }); + }); + + it('should load feed if its not private', function (done) { + request(nconf.get('url') + '/category/' + cid + '.rss', { }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert(body); + done(); + }); + }); + + + it('should not allow access if uid or token is missing', function (done) { + privileges.categories.rescind(['read'], cid, 'guests', function (err) { + assert.ifError(err); + async.parallel({ + test1: function (next) { + request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid, { }, next); + }, + test2: function (next) { + request(nconf.get('url') + '/category/' + cid + '.rss?token=sometoken', { }, next); + }, + }, function (err, results) { + assert.ifError(err); + assert.equal(results.test1[0].statusCode, 200); + assert.equal(results.test2[0].statusCode, 200); + assert(results.test1[0].body.indexOf('Login to your account') !== -1); + assert(results.test2[0].body.indexOf('Login to your account') !== -1); + done(); + }); + }); + }); + + it('should not allow access if token is wrong', function (done) { + request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=sometoken', { }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert(body.indexOf('Login to your account') !== -1); + done(); + }); + }); + + it('should allow access if token is correct', function (done) { + request(nconf.get('url') + '/api/category/' + cid, { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + rssToken = body.rssFeedUrl.split('token')[1].slice(1); + request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=' + rssToken, { }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert(body); + done(); + }); + }); + }); + + it('should not allow access if token is correct but has no privilege', function (done) { + privileges.categories.rescind(['read'], cid, 'registered-users', function (err) { + assert.ifError(err); + request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=' + rssToken, { }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert(body.indexOf('Login to your account') !== -1); + done(); + }); + }); + }); + }); }); diff --git a/test/groups.js b/test/groups.js index ceba296c61..e914953e10 100644 --- a/test/groups.js +++ b/test/groups.js @@ -1138,7 +1138,7 @@ describe('Groups', function () { }); it('should error if user is not owner of group', function (done) { - helpers.loginUser('regularuser', '123456', function (err, jar, io, csrf_token) { + helpers.loginUser('regularuser', '123456', function (err, jar, csrf_token) { assert.ifError(err); helpers.uploadFile(nconf.get('url') + '/api/groups/uploadpicture', logoPath, { params: JSON.stringify({ groupName: 'Test' }) }, jar, csrf_token, function (err, res, body) { assert.ifError(err); @@ -1150,7 +1150,7 @@ describe('Groups', function () { }); it('should upload group cover with api route', function (done) { - helpers.loginUser('admin', '123456', function (err, jar, io, csrf_token) { + helpers.loginUser('admin', '123456', function (err, jar, csrf_token) { assert.ifError(err); helpers.uploadFile(nconf.get('url') + '/api/groups/uploadpicture', logoPath, { params: JSON.stringify({ groupName: 'Test' }) }, jar, csrf_token, function (err, res, body) { assert.ifError(err); diff --git a/test/helpers/index.js b/test/helpers/index.js index c589c7cdd5..29a66e00dd 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -36,9 +36,7 @@ helpers.loginUser = function (username, password, callback) { if (err || res.statusCode !== 200) { return callback(err || new Error('[[error:invalid-response]]')); } - helpers.connectSocketIO(res, function (err, io) { - callback(err, jar, io, body.csrf_token); - }); + callback(null, jar, body.csrf_token); }); }); }; @@ -94,22 +92,6 @@ helpers.connectSocketIO = function (res, callback) { }); }; -helpers.initSocketIO = function (callback) { - var jar; - request.get({ - url: nconf.get('url') + '/api/config', - jar: jar, - json: true, - }, function (err, res) { - if (err) { - return callback(err); - } - helpers.connectSocketIO(res, function (err, io) { - callback(err, jar, io); - }); - }); -}; - helpers.uploadFile = function (uploadEndPoint, filePath, body, jar, csrf_token, callback) { var formData = { files: [ diff --git a/test/logger.js b/test/logger.js deleted file mode 100644 index 6dbdc5f547..0000000000 --- a/test/logger.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -/* global require, after, before*/ - -var assert = require('assert'); -var path = require('path'); -var fs = require('fs'); - -var db = require('./mocks/databasemock'); -var logger = require('../src/logger'); -var index = require('../src/socket.io'); -var meta = require('../src/meta'); -var user = require('../src/user'); -var helpers = require('./helpers'); - -describe('logger', function () { - var jar; - var io; - before(function (done) { - user.create({ username: 'loggeruser', password: '123456' }, function (err) { - assert.ifError(err); - helpers.loginUser('loggeruser', '123456', function (err, _jar, _io) { - assert.ifError(err); - jar = _jar; - io = _io; - done(); - }); - }); - }); - - it('should enable logging', function (done) { - meta.config.loggerStatus = 1; - meta.config.loggerIOStatus = 1; - var loggerPath = path.join(__dirname, '..', 'logs', 'logger.log'); - logger.monitorConfig({ io: index.server }, { key: 'loggerPath', value: loggerPath }); - setTimeout(function () { - io.emit('meta.rooms.enter', { enter: 'recent_topics' }, function (err) { - assert.ifError(err); - fs.readFile(loggerPath, 'utf-8', function (err, content) { - assert.ifError(err); - assert(content); - done(); - }); - }); - }, 500); - }); - - after(function (done) { - meta.config.loggerStatus = 0; - meta.config.loggerIOStatus = 0; - done(); - }); -}); diff --git a/test/messaging.js b/test/messaging.js index d6426812fe..cc8283698b 100644 --- a/test/messaging.js +++ b/test/messaging.js @@ -591,10 +591,11 @@ describe('Messaging Library', function () { }); it('should redirect to chats page', function (done) { - request(nconf.get('url') + '/api/chats', { jar: jar }, function (err, response, body) { + request(nconf.get('url') + '/api/chats', { jar: jar, json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(body, '"/user/herp/chats"'); - assert.equal(response.statusCode, 308); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/user/herp/chats'); + assert.equal(body, '/user/herp/chats'); done(); }); }); diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index 27dab2b647..c43b01a900 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -110,7 +110,7 @@ before(function (done) { nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); - nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); + nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); nconf.set('core_templates_path', path.join(__dirname, '../../src/views')); diff --git a/test/socket.io.js b/test/socket.io.js index 52d8ebc1fe..547895f970 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -576,7 +576,7 @@ describe('socket.io', function () { }); }); - it('shoudl delete all events', function (done) { + it('should delete all events', function (done) { socketAdmin.deleteAllEvents({ uid: adminUid }, {}, function (err) { assert.ifError(err); db.sortedSetCard('events:time', function (err, count) { @@ -586,5 +586,34 @@ describe('socket.io', function () { }); }); }); -}); + describe('logger', function () { + var logger = require('../src/logger'); + var index = require('../src/socket.io'); + var fs = require('fs'); + var path = require('path'); + + it('should enable logging', function (done) { + meta.config.loggerStatus = 1; + meta.config.loggerIOStatus = 1; + var loggerPath = path.join(__dirname, '..', 'logs', 'logger.log'); + logger.monitorConfig({ io: index.server }, { key: 'loggerPath', value: loggerPath }); + setTimeout(function () { + io.emit('meta.rooms.enter', { enter: 'recent_topics' }, function (err) { + assert.ifError(err); + fs.readFile(loggerPath, 'utf-8', function (err, content) { + assert.ifError(err); + assert(content); + done(); + }); + }); + }, 500); + }); + + after(function (done) { + meta.config.loggerStatus = 0; + meta.config.loggerIOStatus = 0; + done(); + }); + }); +}); diff --git a/test/topics.js b/test/topics.js index da3d148e70..a1d0bda512 100644 --- a/test/topics.js +++ b/test/topics.js @@ -7,7 +7,9 @@ var nconf = require('nconf'); var db = require('./mocks/databasemock'); var topics = require('../src/topics'); +var posts = require('../src/posts'); var categories = require('../src/categories'); +var privileges = require('../src/privileges'); var meta = require('../src/meta'); var User = require('../src/user'); var groups = require('../src/groups'); @@ -789,10 +791,11 @@ describe('Topic\'s', function () { }); it('should redirect if post index is out of range', function (done) { - request(nconf.get('url') + '/api/topic/' + topicData.slug + '/-1', function (err, response, body) { + request(nconf.get('url') + '/api/topic/' + topicData.slug + '/-1', { json: true }, function (err, res, body) { assert.ifError(err); - assert.equal(response.statusCode, 308); - assert.equal(body, '"/topic/13/topic-for-controller-test"'); + assert.equal(res.statusCode, 200); + assert.equal(res.headers['x-redirect'], '/topic/13/topic-for-controller-test'); + assert.equal(body, '/topic/13/topic-for-controller-test'); done(); }); }); @@ -825,7 +828,7 @@ describe('Topic\'s', function () { }); it('should 404 if tid is not a number', function (done) { - request(nconf.get('url') + '/api/topic/teaser/nan', { json: true }, function (err, response, body) { + request(nconf.get('url') + '/api/topic/teaser/nan', { json: true }, function (err, response) { assert.ifError(err); assert.equal(response.statusCode, 404); done(); @@ -858,7 +861,7 @@ describe('Topic\'s', function () { it('should 404 if tid is not a number', function (done) { - request(nconf.get('url') + '/api/topic/pagination/nan', { json: true }, function (err, response, body) { + request(nconf.get('url') + '/api/topic/pagination/nan', { json: true }, function (err, response) { assert.ifError(err); assert.equal(response.statusCode, 404); done(); @@ -866,7 +869,7 @@ describe('Topic\'s', function () { }); it('should 404 if tid does not exist', function (done) { - request(nconf.get('url') + '/api/topic/pagination/1231231', { json: true }, function (err, response, body) { + request(nconf.get('url') + '/api/topic/pagination/1231231', { json: true }, function (err, response) { assert.ifError(err); assert.equal(response.statusCode, 404); done(); @@ -1643,4 +1646,63 @@ describe('Topic\'s', function () { }); }); }); + + describe('tag privilege', function () { + var uid; + var cid; + before(function (done) { + async.waterfall([ + function (next) { + User.create({ username: 'tag_poster' }, next); + }, + function (_uid, next) { + uid = _uid; + categories.create({ name: 'tag category' }, next); + }, + function (categoryObj, next) { + cid = categoryObj.cid; + next(); + }, + ], done); + }); + + it('should fail to post if user does not have tag privilege', function (done) { + privileges.categories.rescind(['topics:tag'], cid, 'registered-users', function (err) { + assert.ifError(err); + topics.post({ uid: uid, cid: cid, tags: ['tag1'], title: 'topic with tags', content: 'some content here' }, function (err) { + assert.equal(err.message, '[[error:no-privileges]]'); + done(); + }); + }); + }); + + it('should fail to edit if user does not have tag privilege', function (done) { + topics.post({ uid: uid, cid: cid, title: 'topic with tags', content: 'some content here' }, function (err, result) { + assert.ifError(err); + var pid = result.postData.pid; + posts.edit({ pid: pid, uid: uid, content: 'edited content', tags: ['tag2'] }, function (err) { + assert.equal(err.message, '[[error:no-privileges]]'); + done(); + }); + }); + }); + + it('should be able to edit topic and add tags if allowed', function (done) { + privileges.categories.give(['topics:tag'], cid, 'registered-users', function (err) { + assert.ifError(err); + topics.post({ uid: uid, cid: cid, tags: ['tag1'], title: 'topic with tags', content: 'some content here' }, function (err, result) { + assert.ifError(err); + posts.edit({ pid: result.postData.pid, uid: uid, content: 'edited content', tags: ['tag1', 'tag2'] }, function (err, result) { + assert.ifError(err); + var tags = result.topic.tags.map(function (tag) { + return tag.value; + }); + assert(tags.indexOf('tag1') !== -1); + assert(tags.indexOf('tag2') !== -1); + done(); + }); + }); + }); + }); + }); }); diff --git a/test/uploads.js b/test/uploads.js index ca8e3b1bea..318428c298 100644 --- a/test/uploads.js +++ b/test/uploads.js @@ -4,6 +4,7 @@ var async = require('async'); var assert = require('assert'); var nconf = require('nconf'); var path = require('path'); +var request = require('request'); var db = require('./mocks/databasemock'); var categories = require('../src/categories'); @@ -57,7 +58,7 @@ describe('Upload Controllers', function () { var csrf_token; before(function (done) { - helpers.loginUser('regular', 'zugzug', function (err, _jar, io, _csrf_token) { + helpers.loginUser('regular', 'zugzug', function (err, _jar, _csrf_token) { assert.ifError(err); jar = _jar; csrf_token = _csrf_token; @@ -155,7 +156,7 @@ describe('Upload Controllers', function () { var csrf_token; before(function (done) { - helpers.loginUser('admin', 'barbar', function (err, _jar, io, _csrf_token) { + helpers.loginUser('admin', 'barbar', function (err, _jar, _csrf_token) { assert.ifError(err); jar = _jar; csrf_token = _csrf_token; @@ -247,12 +248,19 @@ describe('Upload Controllers', function () { }); it('should upload touch icon', function (done) { + var touchiconAssetPath = '/assets/uploads/system/touchicon-orig.png'; helpers.uploadFile(nconf.get('url') + '/api/admin/uploadTouchIcon', path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, function (err, res, body) { assert.ifError(err); assert.equal(res.statusCode, 200); assert(Array.isArray(body)); - assert.equal(body[0].url, '/assets/uploads/system/touchicon-orig.png'); - done(); + assert.equal(body[0].url, touchiconAssetPath); + meta.config['brand:touchIcon'] = touchiconAssetPath; + request(nconf.get('url') + '/apple-touch-icon', function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert(body); + done(); + }); }); }); }); diff --git a/test/user.js b/test/user.js index b710863a4b..a7204b01ca 100644 --- a/test/user.js +++ b/test/user.js @@ -155,6 +155,51 @@ describe('User', function () { }); }); + describe('.getModeratorUids()', function () { + before(function (done) { + groups.join('cid:1:privileges:moderate', 1, done); + }); + + it('should retrieve all users with moderator bit in category privilege', function (done) { + User.getModeratorUids(function (err, uids) { + assert.ifError(err); + assert.strictEqual(1, uids.length); + assert.strictEqual(1, parseInt(uids[0], 10)); + done(); + }); + }); + + after(function (done) { + groups.leave('cid:1:privileges:moderate', 1, done); + }); + }); + + describe('.getModeratorUids()', function () { + before(function (done) { + async.series([ + async.apply(groups.create, { name: 'testGroup' }), + async.apply(groups.join, 'cid:1:privileges:groups:moderate', 'testGroup'), + async.apply(groups.join, 'testGroup', 1), + ], done); + }); + + it('should retrieve all users with moderator bit in category privilege', function (done) { + User.getModeratorUids(function (err, uids) { + assert.ifError(err); + assert.strictEqual(1, uids.length); + assert.strictEqual(1, parseInt(uids[0], 10)); + done(); + }); + }); + + after(function (done) { + async.series([ + async.apply(groups.leave, 'cid:1:privileges:groups:moderate', 'testGroup'), + async.apply(groups.destroy, 'testGroup'), + ], done); + }); + }); + describe('.isReadyToPost()', function () { it('should error when a user makes two posts in quick succession', function (done) { Meta.config = Meta.config || {}; @@ -441,22 +486,21 @@ describe('User', function () { done(); }); }); - }); - describe('not logged in', function () { - var jar; - var io; - before(function (done) { - helpers.initSocketIO(function (err, _jar, _io) { + it('should get user data even if one uid is NaN', function (done) { + User.getUsersData([NaN, testUid], function (err, data) { assert.ifError(err); - jar = _jar; - io = _io; + assert.equal(data[0], null); + assert(data[1]); + assert.equal(data[1].username, userData.username); done(); }); }); + }); + describe('not logged in', function () { it('should return error if not logged in', function (done) { - io.emit('user.updateProfile', {}, function (err) { + socketUser.updateProfile({ uid: 0 }, {}, function (err) { assert.equal(err.message, '[[error:invalid-uid]]'); done(); }); @@ -466,30 +510,28 @@ describe('User', function () { describe('profile methods', function () { var uid; var jar; - var io; before(function (done) { User.create({ username: 'updateprofile', email: 'update@me.com', password: '123456' }, function (err, newUid) { assert.ifError(err); uid = newUid; - helpers.loginUser('updateprofile', '123456', function (err, _jar, _io) { + helpers.loginUser('updateprofile', '123456', function (err, _jar) { assert.ifError(err); jar = _jar; - io = _io; done(); }); }); }); it('should return error if data is invalid', function (done) { - io.emit('user.updateProfile', null, function (err) { + socketUser.updateProfile({ uid: uid }, null, function (err) { assert.equal(err.message, '[[error:invalid-data]]'); done(); }); }); it('should return error if data is missing uid', function (done) { - io.emit('user.updateProfile', { username: 'bip', email: 'bop' }, function (err) { + socketUser.updateProfile({ uid: uid }, { username: 'bip', email: 'bop' }, function (err) { assert.equal(err.message, '[[error:invalid-data]]'); done(); }); @@ -507,7 +549,7 @@ describe('User', function () { birthday: '01/01/1980', signature: 'nodebb is good', }; - io.emit('user.updateProfile', data, function (err, result) { + socketUser.updateProfile({ uid: uid }, data, function (err, result) { assert.ifError(err); assert.equal(result.username, 'updatedUserName'); @@ -539,7 +581,7 @@ describe('User', function () { }); it('should change username', function (done) { - io.emit('user.changeUsernameEmail', { uid: uid, username: 'updatedAgain', password: '123456' }, function (err) { + socketUser.changeUsernameEmail({ uid: uid }, { uid: uid, username: 'updatedAgain', password: '123456' }, function (err) { assert.ifError(err); db.getObjectField('user:' + uid, 'username', function (err, username) { assert.ifError(err); @@ -550,7 +592,7 @@ describe('User', function () { }); it('should change email', function (done) { - io.emit('user.changeUsernameEmail', { uid: uid, email: 'updatedAgain@me.com', password: '123456' }, function (err) { + socketUser.changeUsernameEmail({ uid: uid }, { uid: uid, email: 'updatedAgain@me.com', password: '123456' }, function (err) { assert.ifError(err); db.getObjectField('user:' + uid, 'email', function (err, email) { assert.ifError(err); @@ -563,7 +605,7 @@ describe('User', function () { it('should update cover image', function (done) { var imageData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAYAAAABtRhCAAAACXBIWXMAAC4jAAAuIwF4pT92AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAACcJJREFUeNqMl9tvnNV6xn/f+s5z8DCeg88Zj+NYdhJH4KShFoJAIkzVphLVJnsDaiV6gUKaC2qQUFVATbnoValAakuQYKMqBKUUJCgI9XBBSmOROMqGoCStHbA9sWM7nrFn/I3n9B17kcwoabfarj9gvet53+d9nmdJAwMDAAgh8DyPtbU1XNfFMAwkScK2bTzPw/M8dF1/SAhxKAiCxxVF2aeqqqTr+q+Af+7o6Ch0d3f/69TU1KwkSRiGwbFjx3jmmWd47rnn+OGHH1BVFYX/5QRBkPQ87xeSJP22YRi/oapqStM0PM/D931kWSYIgnHf98cXFxepVqtomjZt2/Zf2bb990EQ4Pv+PXfeU1CSpGYhfN9/TgjxQTQaJQgCwuEwQRBQKpUwDAPTNPF9n0ajAYDv+8zPzzM+Pr6/Wq2eqdVqfxOJRA6Zpnn57hrivyEC0IQQZ4Mg+MAwDCKRCJIkUa/XEUIQi8XQNI1QKIQkSQghUBQFIQSmaTI7OwtAuVxOTE9Pfzc9Pf27lUqlBUgulUoUi0VKpRKqqg4EQfAfiqLsDIfDAC0E4XCYaDSKEALXdalUKvfM1/d9hBBYlkUul2N4eJi3335bcl33mW+++aaUz+cvSJKE8uKLL6JpGo7j8Omnn/7d+vp6sr+/HyEEjuMgyzKu6yJJEsViEVVV8TyPjY2NVisV5fZkTNMkkUhw8+ZN6vU6Kysr7Nmzh9OnT7/12GOPDS8sLByT7rQR4A9XV1d/+cILLzA9PU0kEmF4eBhFUTh//jyWZaHrOkII0uk0jUaDWq1GJpOhWCyysrLC1tYWnuehqir79+9H13W6urp48803+f7773n++ef/4G7S/H4ikUCSJNbX11trcuvWLcrlMrIs4zgODzzwABMTE/i+T7lcpq2tjUqlwubmJrZts7y8jBCCkZERGo0G2WyWkydPkkql6Onp+eMmwihwc3JyMvrWW2+RTCYBcF0XWZbRdZ3l5WX27NnD008/TSwWQ1VVyuVy63GhUIhEIkEqlcJxHCzLIhaLMTQ0xJkzZ7Btm3379lmS53kIIczZ2dnFsbGxRK1Wo729HQDP8zAMg5WVFXp7e5mcnKSzs5N8Po/rutTrdVzXbQmHrutEo1FM00RVVXp7e0kkEgRBwMWLF9F1vaxUq1UikUjtlVdeuV6pVBJ9fX3Ytn2bwrLMysoKXV1dTE5OkslksCwLTdMwDANVVdnY2CAIApLJJJFIBMdxiMfj7Nq1C1VViUajLQCvvvrqkhKJRJiZmfmdb7/99jeTySSyLLfWodFoEAqFOH78OLt37yaXy2GaJoqisLy8zNTUFFevXiUIAtrb29m5cyePPPJIa+cymQz1eh2A0dFRCoXCsgIwNTW1J5/P093dTbFYRJZlJEmiWq1y4MABxsbGqNVqhEIh6vU6QRBQLpcxDIPh4WE8z2NxcZFTp05x7tw5Xn755ZY6dXZ2tliZzWa/EwD1ev3RsbExxsfHSafTVCoVGo0Gqqqya9cuIpEIQgh832dtbY3FxUUA+vr62LZtG2NjYxw5coTDhw+ztLTEyZMnuXr1KoVC4R4d3bt375R84sQJEY/H/2Jubq7N9326urqwbZt6vY5pmhw5coS+vr4W9YvFIrdu3WJqagohBFeuXOHcuXOtue7evRtN01rtfO+991haWmJkZGQrkUi8JIC9iqL0BkFAIpFACMETTzxBV1cXiUSC7u5uHMfB8zyCIMA0TeLxONlsFlmW8X2fwcFBHMdhfn6eer1Oe3s7Dz30EBMTE1y6dImjR49y6tSppR07dqwrjuM8+OWXXzI0NMTly5e5du0aQ0NDTExMkMvlCIKAIAhaIh2LxQiHw0QiEfL5POl0mlqtRq1Wo6OjA8uykGWZdDrN0tISvb29vPPOOzz++OPk83lELpf7rXfffRfDMOjo6MBxHEqlEocOHWLHjh00Gg0kSULTNIS4bS6qqhKPxxkaGmJ4eJjR0VH279/PwMAA27dvJ5vN4vs+X331FR9//DGzs7OEQiE++eQTlPb29keuX7/OtWvXOH78ONVqlZs3b9LW1kYmk8F13dZeCiGQJAnXdRFCYBgGsiwjhMC2bQqFAkEQoOs6P/74Iw8++CCDg4Pous6xY8f47LPPkIIguDo2Nrbzxo0bfPjhh9i2zczMTHNvcF2XpsZalkWj0cB1Xe4o1O3YoCisra3x008/EY/H6erqAuDAgQNEIhGCIODQoUP/ubCwMCKAjx599FHW19f56KOP6OjooFgsks/niUajKIqCbds4joMQAiFESxxs226xd2Zmhng8Tl9fH67r0mg0sG2bbDZLpVIhl8vd5gHwtysrKy8Dcdd1mZubo6enh1gsRrVabZlrk6VND/R9n3q9TqVSQdd1QqEQi4uLnD9/nlKpxODgIHv37gXAcRyCICiFQiHEzp07i1988cUfKYpCIpHANE22b9/eUhNFUVotDIKghc7zPCzLolKpsLW1RVtbG0EQ4DgOmqbR09NDM1qUSiWAPwdQ7ujjmf7+/kQymfxrSZJQVZWtra2WG+i63iKH53m4rku1WqVcLmNZFu3t7S2x7+/vJ51O89prr7VYfenSpcPAP1UqFeSHH36YeDxOKpW6eP/9988Bv9d09nw+T7VapVKptJjZnE2tVmNtbY1cLke5XGZra4vNzU16enp49tlnGRgYaD7iTxqNxgexWIzDhw+jNEPQHV87NT8/f+PChQtnR0ZGqFarrUVuOsDds2u2b2FhgVQqRSQSYWFhgStXrtDf308ymcwBf3nw4EEOHjx4O5c2lURVVRzHYXp6+t8uX7785IULFz7LZDLous59991HOBy+h31N9xgdHSWTyVCtVhkaGmLfvn1MT08zPz/PzMzM6c8//9xr+uE9QViWZer1OhsbGxiG8fns7OzPc7ncx729vXR3d1OpVNi2bRuhUAhZljEMA9/3sW0bVVVZWlri4sWLjI+P8/rrr/P111/z5JNPXrIs69cn76ZeGoaBpmm0tbX9Q6FQeHhubu7fC4UCkUiE1dVVstks8Xgc0zSRZZlGo9ESAdM02djYoNFo8MYbb2BZ1mYoFOKuZPjr/xZBEHCHred83x/b3Nz8l/X19aRlWWxsbNDZ2cnw8DDhcBjf96lWq/T09HD06FGeeuopXnrpJc6ePUs6nb4hhPi/C959ZFn+TtO0lG3bJ0ql0p85jsPW1haFQoG2tjYkSWpF/Uwmw9raGu+//z7A977vX2+GrP93wSZiTdNOGIbxy3K5/DPHcfYXCoVe27Yzpmm2m6bppVKp/Orqqnv69OmoZVn/mEwm/9TzvP9x138NAMpJ4VFTBr6SAAAAAElFTkSuQmCC'; var position = '50.0301% 19.2464%'; - io.emit('user.updateCover', { uid: uid, imageData: imageData, position: position }, function (err, result) { + socketUser.updateCover({ uid: uid }, { uid: uid, imageData: imageData, position: position }, function (err, result) { assert.ifError(err); assert(result.url); db.getObjectFields('user:' + uid, ['cover:url', 'cover:position'], function (err, data) { @@ -590,7 +632,7 @@ describe('User', function () { }); it('should remove cover image', function (done) { - io.emit('user.removeCover', { uid: uid }, function (err) { + socketUser.removeCover({ uid: uid }, { uid: uid }, function (err) { assert.ifError(err); db.getObjectField('user:' + uid, 'cover:url', function (err, url) { assert.ifError(err); @@ -601,7 +643,7 @@ describe('User', function () { }); it('should set user status', function (done) { - io.emit('user.setStatus', 'away', function (err, data) { + socketUser.setStatus({ uid: uid }, 'away', function (err, data) { assert.ifError(err); assert.equal(data.uid, uid); assert.equal(data.status, 'away'); @@ -610,14 +652,14 @@ describe('User', function () { }); it('should fail for invalid status', function (done) { - io.emit('user.setStatus', '12345', function (err) { + socketUser.setStatus({ uid: uid }, '12345', function (err) { assert.equal(err.message, '[[error:invalid-user-status]]'); done(); }); }); it('should get user status', function (done) { - io.emit('user.checkStatus', uid, function (err, status) { + socketUser.checkStatus({ uid: uid }, uid, function (err, status) { assert.ifError(err); assert.equal(status, 'away'); done(); @@ -625,7 +667,7 @@ describe('User', function () { }); it('should change user picture', function (done) { - io.emit('user.changePicture', { type: 'default', uid: uid }, function (err) { + socketUser.changePicture({ uid: uid }, { type: 'default', uid: uid }, function (err) { assert.ifError(err); User.getUserField(uid, 'picture', function (err, picture) { assert.ifError(err); @@ -792,7 +834,7 @@ describe('User', function () { }); it('should get profile pictures', function (done) { - io.emit('user.getProfilePictures', { uid: uid }, function (err, data) { + socketUser.getProfilePictures({ uid: uid }, { uid: uid }, function (err, data) { assert.ifError(err); assert(data); assert(Array.isArray(data)); @@ -825,7 +867,7 @@ describe('User', function () { }); it('should remove uploaded picture', function (done) { - io.emit('user.removeUploadedPicture', { uid: uid }, function (err) { + socketUser.removeUploadedPicture({ uid: uid }, { uid: uid }, function (err) { assert.ifError(err); User.getUserField(uid, 'uploadedpicture', function (err, uploadedpicture) { assert.ifError(err); @@ -994,20 +1036,31 @@ describe('User', function () { describe('digests', function () { var uid; before(function (done) { - User.create({ username: 'digestuser', email: 'test@example.com' }, function (err, _uid) { + async.waterfall([ + function (next) { + User.create({ username: 'digestuser', email: 'test@example.com' }, next); + }, + function (_uid, next) { + uid = _uid; + User.updateDigestSetting(uid, 'day', next); + }, + function (next) { + User.setSetting(uid, 'dailyDigestFreq', 'day', next); + }, + ], done); + }); + + it('should send digests', function (done) { + User.digest.execute({ interval: 'day' }, function (err) { assert.ifError(err); - uid = _uid; done(); }); }); - it('should send digests', function (done) { - User.updateDigestSetting(uid, 'day', function (err) { + it('should not send digests', function (done) { + User.digest.execute({ interval: 'month' }, function (err) { assert.ifError(err); - User.digest.execute('day', function (err) { - assert.ifError(err); - done(); - }); + done(); }); }); }); @@ -1472,7 +1525,7 @@ describe('User', function () { it('should send digest', function (done) { db.sortedSetAdd('digest:day:uids', [Date.now(), Date.now()], [1, 2], function (err) { assert.ifError(err); - User.digest.execute('day', function (err) { + User.digest.execute({ interval: 'day' }, function (err) { assert.ifError(err); done(); });