diff --git a/.tx/config b/.tx/config index d8dce705c9..3bebe996b1 100644 --- a/.tx/config +++ b/.tx/config @@ -7,6 +7,7 @@ source_file = public/language/en_GB/category.json source_lang = en_GB trans.ar = public/language/ar/category.json trans.bn = public/language/bn/category.json +trans.bg = public/language/bg/category.json trans.cs = public/language/cs/category.json trans.de = public/language/de/category.json trans.el = public/language/el/category.json @@ -47,6 +48,7 @@ source_file = public/language/en_GB/login.json source_lang = en_GB trans.ar = public/language/ar/login.json trans.bn = public/language/bn/login.json +trans.bg = public/language/bg/login.json trans.cs = public/language/cs/login.json trans.de = public/language/de/login.json trans.el = public/language/el/login.json @@ -86,6 +88,7 @@ source_file = public/language/en_GB/recent.json source_lang = en_GB trans.ar = public/language/ar/recent.json trans.bn = public/language/bn/recent.json +trans.bg = public/language/bg/recent.json trans.cs = public/language/cs/recent.json trans.de = public/language/de/recent.json trans.el = public/language/el/recent.json @@ -125,6 +128,7 @@ source_file = public/language/en_GB/unread.json source_lang = en_GB trans.ar = public/language/ar/unread.json trans.bn = public/language/bn/unread.json +trans.bg = public/language/bg/unread.json trans.cs = public/language/cs/unread.json trans.de = public/language/de/unread.json trans.el = public/language/el/unread.json @@ -164,6 +168,7 @@ source_file = public/language/en_GB/modules.json source_lang = en_GB trans.ar = public/language/ar/modules.json trans.bn = public/language/bn/modules.json +trans.bg = public/language/bg/modules.json trans.cs = public/language/cs/modules.json trans.de = public/language/de/modules.json trans.el = public/language/el/modules.json @@ -203,6 +208,7 @@ source_file = public/language/en_GB/register.json source_lang = en_GB trans.ar = public/language/ar/register.json trans.bn = public/language/bn/register.json +trans.bg = public/language/bg/register.json trans.cs = public/language/cs/register.json trans.de = public/language/de/register.json trans.el = public/language/el/register.json @@ -242,6 +248,7 @@ source_file = public/language/en_GB/user.json source_lang = en_GB trans.ar = public/language/ar/user.json trans.bn = public/language/bn/user.json +trans.bg = public/language/bg/user.json trans.cs = public/language/cs/user.json trans.de = public/language/de/user.json trans.el = public/language/el/user.json @@ -281,6 +288,7 @@ source_file = public/language/en_GB/global.json source_lang = en_GB trans.ar = public/language/ar/global.json trans.bn = public/language/bn/global.json +trans.bg = public/language/bg/global.json trans.cs = public/language/cs/global.json trans.de = public/language/de/global.json trans.el = public/language/el/global.json @@ -320,6 +328,7 @@ source_file = public/language/en_GB/notifications.json source_lang = en_GB trans.ar = public/language/ar/notifications.json trans.bn = public/language/bn/notifications.json +trans.bg = public/language/bg/notifications.json trans.cs = public/language/cs/notifications.json trans.de = public/language/de/notifications.json trans.el = public/language/el/notifications.json @@ -359,6 +368,7 @@ source_file = public/language/en_GB/reset_password.json source_lang = en_GB trans.ar = public/language/ar/reset_password.json trans.bn = public/language/bn/reset_password.json +trans.bg = public/language/bg/reset_password.json trans.cs = public/language/cs/reset_password.json trans.de = public/language/de/reset_password.json trans.el = public/language/el/reset_password.json @@ -398,6 +408,7 @@ source_file = public/language/en_GB/users.json source_lang = en_GB trans.ar = public/language/ar/users.json trans.bn = public/language/bn/users.json +trans.bg = public/language/bg/users.json trans.cs = public/language/cs/users.json trans.de = public/language/de/users.json trans.el = public/language/el/users.json @@ -437,6 +448,7 @@ source_file = public/language/en_GB/language.json source_lang = en_GB trans.ar = public/language/ar/language.json trans.bn = public/language/bn/language.json +trans.bg = public/language/bg/language.json trans.cs = public/language/cs/language.json trans.de = public/language/de/language.json trans.el = public/language/el/language.json @@ -476,6 +488,7 @@ source_file = public/language/en_GB/pages.json source_lang = en_GB trans.ar = public/language/ar/pages.json trans.bn = public/language/bn/pages.json +trans.bg = public/language/bg/pages.json trans.cs = public/language/cs/pages.json trans.de = public/language/de/pages.json trans.el = public/language/el/pages.json @@ -515,6 +528,7 @@ source_file = public/language/en_GB/topic.json source_lang = en_GB trans.ar = public/language/ar/topic.json trans.bn = public/language/bn/topic.json +trans.bg = public/language/bg/topic.json trans.cs = public/language/cs/topic.json trans.de = public/language/de/topic.json trans.el = public/language/el/topic.json @@ -554,6 +568,7 @@ source_file = public/language/en_GB/success.json source_lang = en_GB trans.ar = public/language/ar/success.json trans.bn = public/language/bn/success.json +trans.bg = public/language/bg/success.json trans.cs = public/language/cs/success.json trans.de = public/language/de/success.json trans.el = public/language/el/success.json @@ -593,6 +608,7 @@ source_file = public/language/en_GB/error.json source_lang = en_GB trans.ar = public/language/ar/error.json trans.bn = public/language/bn/error.json +trans.bg = public/language/bg/error.json trans.cs = public/language/cs/error.json trans.de = public/language/de/error.json trans.el = public/language/el/error.json @@ -632,6 +648,7 @@ source_file = public/language/en_GB/tags.json source_lang = en_GB trans.ar = public/language/ar/tags.json trans.bn = public/language/bn/tags.json +trans.bg = public/language/bg/tags.json trans.cs = public/language/cs/tags.json trans.de = public/language/de/tags.json trans.el = public/language/el/tags.json @@ -671,6 +688,7 @@ source_file = public/language/en_GB/email.json source_lang = en_GB trans.ar = public/language/ar/email.json trans.bn = public/language/bn/email.json +trans.bg = public/language/bg/email.json trans.cs = public/language/cs/email.json trans.de = public/language/de/email.json trans.el = public/language/el/email.json @@ -710,6 +728,7 @@ source_file = public/language/en_GB/search.json source_lang = en_GB trans.ar = public/language/ar/search.json trans.bn = public/language/bn/search.json +trans.bg = public/language/bg/search.json trans.cs = public/language/cs/search.json trans.de = public/language/de/search.json trans.el = public/language/el/search.json @@ -749,6 +768,7 @@ source_file = public/language/en_GB/groups.json source_lang = en_GB trans.ar = public/language/ar/groups.json trans.bn = public/language/bn/groups.json +trans.bg = public/language/bg/groups.json trans.cs = public/language/cs/groups.json trans.de = public/language/de/groups.json trans.el = public/language/el/groups.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5223929285..d6d64a04a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,11 @@ If you are writing contributions as part of employment from another company / in Chances are somebody has run into this problem before. After consulting our [documentation](https://docs.nodebb.org/en/latest/installing/os.html), please head over to our [community support forum](https://community.nodebb.org) for advice. +# Found a Security Vulnerability? + +If you believe you have identified a security vulnerability with NodeBB, report it as soon as possible via email to **security@nodebb.org**. +A member of the NodeBB security team will respond to the issue. +Please do not post it to the public bug tracker. # Issues & Bugs @@ -20,11 +25,6 @@ Thanks for reporting an issue with NodeBB! Please follow these guidelines in ord In general, if we can't reproduce it, we can't fix it! -> #### **Important** -> If you believe you have identified a security vulnerability with NodeBB, report it as soon as possible via email to **security@nodebb.org**. -> A member of the NodeBB security team will respond to the issue. -> Please do not post it to the public bug tracker. - ## Try the latest version of NodeBB There is a chance that the issue you are experiencing may have already been fixed. diff --git a/Gruntfile.js b/Gruntfile.js index 551d8d7ed3..5820b1565f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -60,7 +60,7 @@ module.exports = function(grunt) { files: ['public/src/**/*.js', 'node_modules/nodebb-*/*.js', 'node_modules/nodebb-*/*/*.js', 'node_modules/nodebb-*/*/*/*.js', 'node_modules/nodebb-*/*/*/*/*.js'] }, serverUpdated: { - files: ['*.js', 'src/**/*.js'] + files: ['*.js', 'install/*.js', 'src/**/*.js'] }, templatesUpdated: { files: ['src/views/**/*.tpl', 'node_modules/nodebb-*/*.tpl', 'node_modules/nodebb-*/*/*.tpl', 'node_modules/nodebb-*/*/*/*.tpl', 'node_modules/nodebb-*/*/*/*/*.tpl', 'node_modules/nodebb-*/*/*/*/*/*.tpl'] diff --git a/README.md b/README.md index d28610fcb9..4684d5c73a 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,6 @@ Additional functionality is enabled through the use of third-party plugins. [](http://i.imgur.com/FLOUuIq.png) [](http://i.imgur.com/Ud1LrfI.png) [](http://i.imgur.com/ZC8W39a.png) [](http://i.imgur.com/o90kVPi.png) [](http://i.imgur.com/AaRRrU2.png) [](http://i.imgur.com/LmHtPho.png) [](http://i.imgur.com/paiJPJk.jpg) [](http://i.imgur.com/ZfavPHD.png) [](http://i.imgur.com/8OLssij.png) [](http://i.imgur.com/JKOc0LZ.png) -Credit: [Convoe](http://www.convoe.com), [Kano](http://www.kano.me), [Manchester United Forum](http://manutdforums.com/). - - ## How can I follow along/contribute? * Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/NodeBB/NodeBB/wiki/Version-History-%26-Roadmap) diff --git a/app.js b/app.js index ef113e5ece..8ff5647023 100644 --- a/app.js +++ b/app.js @@ -21,7 +21,7 @@ /*global require, global, process*/ var nconf = require('nconf'); -nconf.argv().env(); +nconf.argv().env('__'); var fs = require('fs'), os = require('os'), @@ -54,14 +54,6 @@ if(os.platform() === 'linux') { }); } -if (!process.send) { - // If run using `node app`, log GNU copyright info along with server info - winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 NodeBB Inc.'); - winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); - winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); - winston.info(''); -} - // Alternate configuration file support var configFile = path.join(__dirname, '/config.json'), configExists; @@ -73,8 +65,10 @@ configExists = fs.existsSync(configFile); if (!nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) { start(); -} else if (nconf.get('setup') || nconf.get('install') || !configExists) { +} else if (nconf.get('setup') || nconf.get('install')) { setup(); +} else if (!configExists) { + require('./install/web').install(nconf.get('port')); } else if (nconf.get('upgrade')) { upgrade(); } else if (nconf.get('reset')) { @@ -89,7 +83,8 @@ function loadConfig() { nconf.defaults({ base_dir: __dirname, themes_path: path.join(__dirname, 'node_modules'), - views_dir: path.join(__dirname, 'public/templates') + views_dir: path.join(__dirname, 'public/templates'), + version: pkg.version }); if (!nconf.get('isCluster')) { @@ -101,6 +96,14 @@ function loadConfig() { nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path'))); nconf.set('core_templates_path', path.join(__dirname, 'src/views')); nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates')); + + if (!process.send) { + // If run using `node app`, log GNU copyright info along with server info + winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-2014 NodeBB Inc.'); + winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); + winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); + winston.info(''); + } } function start() { @@ -120,7 +123,7 @@ function start() { if (nconf.get('isPrimary') === 'true') { winston.info('Time: %s', (new Date()).toString()); - winston.info('Initializing NodeBB v%s', pkg.version); + winston.info('Initializing NodeBB v%s', nconf.get('version')); winston.verbose('* using configuration stored in: %s', configFile); var host = nconf.get(nconf.get('database') + ':host'), @@ -130,111 +133,118 @@ function start() { winston.verbose('* using themes stored in: %s', nconf.get('themes_path')); } + process.on('SIGTERM', shutdown); + process.on('SIGINT', shutdown); + process.on('SIGHUP', restart); + process.on('message', function(message) { + if (typeof message !== 'object') { + return; + } + var meta = require('./src/meta'); + var emitter = require('./src/emitter'); + switch (message.action) { + case 'reload': + meta.reload(); + break; + case 'js-propagate': + meta.js.cache = message.cache; + meta.js.map = message.map; + meta.js.hash = message.hash; + emitter.emit('meta:js.compiled'); + winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid); + break; + case 'css-propagate': + meta.css.cache = message.cache; + meta.css.acpCache = message.acpCache; + meta.css.hash = message.hash; + emitter.emit('meta:css.compiled'); + winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid); + break; + case 'templates:compiled': + emitter.emit('templates:compiled'); + break; + } + }); - var webserver = require('./src/webserver'); + process.on('uncaughtException', function(err) { + winston.error(err.stack); + console.log(err.stack); - require('./src/database').init(function(err) { + require('./src/meta').js.killMinifier(); + shutdown(1); + }); + + async.waterfall([ + function(next) { + require('./src/database').init(next); + }, + function(next) { + require('./src/meta').configs.init(next); + }, + function(next) { + require('./src/upgrade').check(next); + }, + function(schema_ok, next) { + if (!schema_ok && nconf.get('check-schema') !== false) { + winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); + winston.warn(' ./nodebb upgrade'); + process.exit(); + return; + } + var webserver = require('./src/webserver'); + require('./src/socket.io').init(webserver.server); + + if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) { + require('./src/notifications').init(); + require('./src/user').startJobs(); + } + + webserver.listen(); + } + ], function(err) { if (err) { winston.error(err.stack); process.exit(); } - var meta = require('./src/meta'); - meta.configs.init(function () { - var templates = require('templates.js'), - sockets = require('./src/socket.io'), - plugins = require('./src/plugins'), - upgrade = require('./src/upgrade'); - - templates.setGlobal('relative_path', nconf.get('relative_path')); - - upgrade.check(function(schema_ok) { - if (schema_ok || nconf.get('check-schema') === false) { - webserver.init(); - sockets.init(webserver.server); - - if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) { - require('./src/notifications').init(); - require('./src/user').startJobs(); - } - - async.waterfall([ - async.apply(meta.themes.setupPaths), - async.apply(plugins.ready), - async.apply(meta.templates.compile), - async.apply(webserver.listen) - ], function(err) { - if (err) { - winston.error(err.stack); - process.exit(); - } - - if (process.send) { - process.send({ - action: 'ready' - }); - } - }); - - process.on('SIGTERM', shutdown); - process.on('SIGINT', shutdown); - process.on('SIGHUP', restart); - process.on('message', function(message) { - switch(message.action) { - case 'reload': - meta.reload(); - break; - case 'js-propagate': - meta.js.cache = message.cache; - meta.js.map = message.map; - meta.js.hash = message.hash; - winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid); - break; - case 'css-propagate': - meta.css.cache = message.cache; - meta.css.acpCache = message.acpCache; - meta.css.hash = message.hash; - winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid); - break; - } - }); - - process.on('uncaughtException', function(err) { - winston.error(err.stack); - console.log(err.stack); - - meta.js.killMinifier(); - shutdown(1); - }); - } else { - winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); - winston.warn(' ./nodebb upgrade'); - process.exit(); - } - }); - }); }); } function setup() { loadConfig(); - if (nconf.get('setup')) { - winston.info('NodeBB Setup Triggered via Command Line'); - } else { - winston.warn('Configuration not found, starting NodeBB setup'); - } + winston.info('NodeBB Setup Triggered via Command Line'); var install = require('./src/install'); - winston.info('Welcome to NodeBB!'); - winston.info('This looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.'); - winston.info('Press enter to accept the default setting (shown in brackets).'); + process.stdout.write('\nWelcome to NodeBB!\n'); + process.stdout.write('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.\n'); + process.stdout.write('Press enter to accept the default setting (shown in brackets).\n'); + + install.setup(function (err, data) { + var separator = ' '; + if (process.stdout.columns > 10) { + for(var x=0,cols=process.stdout.columns-10;xВсе още няма теми в тази категория.
Защо не създадеш една?", + "browsing": "Разглежда", + "no_replies": "Все още никой не е отговорил", + "share_this_category": "Споделяне на тази категория", + "watch": "Следене", + "ignore": "Игнориране", + "watch.message": "Вече следите обновленията в тази категория", + "ignore.message": "Вече не следите обновленията в тази категория" +} \ No newline at end of file diff --git a/public/language/bg/email.json b/public/language/bg/email.json new file mode 100644 index 0000000000..3ee542969f --- /dev/null +++ b/public/language/bg/email.json @@ -0,0 +1,28 @@ +{ + "password-reset-requested": "Изпратена е заявка за подновяване на паролата – %1!", + "welcome-to": "Добре дошли в %1", + "greeting_no_name": "Здравейте", + "greeting_with_name": "Здравейте, %1", + "welcome.text1": "Благодарим Ви, че се регистрирахте с %1", + "welcome.text2": "За да активирате напълно Вашия акаунт, трябва да потвърдите е-пощата, с която сте се регистрирали.", + "welcome.cta": "Натиснете тук, за да потвърдите Вашата е-поща.", + "reset.text1": "Получихме заявка за подновяване на Вашата парола, най-вероятно защото сте я забравили. Ако това не е така, моля не обръщайте внимание на това е-писмо.", + "reset.text2": "За да продължите с процедурата по подновяване на паролата, моля последвайте следната връзка:", + "reset.cta": "Натиснете тук, за да подновите паролата си", + "reset.notify.subject": "Паролата беше променена успешно", + "reset.notify.text1": "Известяваме Ви, че на %1, Вашата парола беше променена успешно.", + "reset.notify.text2": "Ако не сте поискали това, моля, свържете се незабавно с администратор.", + "digest.notifications": "Имате непрочетени известия от %1:", + "digest.latest_topics": "Последни теми от %1", + "digest.cta": "Натиснете тук, за да посетите %1", + "digest.unsub.info": "Това резюме беше изпратено до Вас поради настройките Ви за абонаментите.", + "digest.no_topics": "Не е имало дейност по темите в последните %1", + "notif.chat.subject": "Получено е ново чат съобщение от %1", + "notif.chat.cta": "Натиснете тук, за да продължите разговора", + "notif.chat.unsub.info": "Това известие за чата беше изпратено до Вас поради настройките Ви за абонаментите.", + "notif.post.cta": "Натиснете тук, за да прочетете цялата тема", + "notif.post.unsub.info": "Това известие за публикация беше изпратено до Вас поради настройките Ви за абонаментите.", + "test.text1": "Това е пробно е-писмо, за да потвърдим, че изпращачът на е-поща е правилно настроен за Вашия NodeBB.", + "unsub.cta": "Натиснете тук, за да промените тези настройки", + "closing": "Благодарим Ви!" +} \ No newline at end of file diff --git a/public/language/bg/error.json b/public/language/bg/error.json new file mode 100644 index 0000000000..17907c8815 --- /dev/null +++ b/public/language/bg/error.json @@ -0,0 +1,83 @@ +{ + "invalid-data": "Невалидни данни", + "not-logged-in": "Изглежда не сте влезли в системата.", + "account-locked": "Вашият акаунт беше заключен временно", + "search-requires-login": "Търсенето изисква регистриран акаунт! Моля, влезте или се регистрирайте!", + "invalid-cid": "Невалиден идентификатор на категория", + "invalid-tid": "Невалиден идентификатор на тема", + "invalid-pid": "Невалиден идентификатор на публикация", + "invalid-uid": "Невалиден идентификатор на потребител", + "invalid-username": "Невалидно потребителско име", + "invalid-email": "Невалидна е-поща", + "invalid-title": "Невалидно заглавие!", + "invalid-user-data": "Невалидни потребителски данни", + "invalid-password": "Невалидна парола", + "invalid-username-or-password": "Моля, посочете потребителско име и парола", + "invalid-search-term": "Невалиден текст за търсене", + "invalid-pagination-value": "Невалиден номер на страница", + "username-taken": "Потребителското име е заето", + "email-taken": "Е-пощата е заета", + "email-not-confirmed": "Вашата е-поща все още не е потвърдена. Моля, натиснете тук, за да потвърдите е-пощата си.", + "email-not-confirmed-chat": "Няма да можете да пишете в чата, докато е-пощата Ви не бъде потвърдена. Моля, натиснете тук, за да потвърдите е-пощата си.", + "no-email-to-confirm": "Този форум изисква потвърдена е-поща. Моля, натиснете тук, за да въведете е-поща", + "email-confirm-failed": "Не успяхме да потвърдим е-пощата Ви. Моля, опитайте отново по-късно.", + "confirm-email-already-sent": "Е-писмото за потвърждение вече е изпратено. Моля, почакайте още %1 минута/и, преди да изпратите ново.", + "username-too-short": "Потребителското име е твърде кратко", + "username-too-long": "Потребителското име е твърде дълго", + "user-banned": "Потребителят е блокиран", + "user-too-new": "Съжаляваме, но трябва да изчакате поне %1 секунда/и, преди да направите първата си публикация", + "no-category": "Категорията не съществува", + "no-topic": "Темата не съществува", + "no-post": "Публикацията не съществува", + "no-group": "Групата не съществува", + "no-user": "Потребителят не съществува", + "no-teaser": "Резюмето не съществува", + "no-privileges": "Нямате достатъчно права за това действие.", + "no-emailers-configured": "Добавките за е-поща не са заредени, така че не може да бъде изпратено пробно е-писмо", + "category-disabled": "Категорията е изключена", + "topic-locked": "Темата е заключена", + "post-edit-duration-expired": "Можете да редактирате публикациите си до %1 секунда/и, след като ги пуснете", + "still-uploading": "Моля, изчакайте качването да приключи.", + "content-too-short": "Моля, въведете по-дълъг текст на публикацията. Публикациите трябва да съдържат поне %1 символ(а).", + "content-too-long": "Моля, въведете по-кратък текст на публикацията. Публикациите трябва да съдържат не повече от %1 символ(а).", + "title-too-short": "Моля, въведете по-дълго заглавие. Заглавията трябва да съдържат поне %1 символ(а).", + "title-too-long": "Моля, въведете по-кратко заглавие. Заглавията трябва да съдържат не повече от %1 символ(а).", + "too-many-posts": "Можете да публикувате веднъж на %1 секунда/и – моля, изчакайте малко, преди да опитате да публикувате отново", + "too-many-posts-newbie": "Като нов потребител, Вие можете да публикувате веднъж на %1 секунда/и, докато не натрупате %2 репутация – моля, изчакайте малко, преди да опитате да публикувате отново", + "tag-too-short": "Моля, въведете по-дълъг етикет. Етикетите трябва да съдържат поне %1 символ(а)", + "tag-too-long": "Моля, въведете по-кратък етикет. Етикетите трябва да съдържат не повече от %1 символ(а)", + "file-too-big": "Максималният разрешен размер на файл е %1 КБ – моля, качете по-малък файл", + "cant-vote-self-post": "Не можете да гласувате за собствената си публикация", + "already-favourited": "Вече сте отбелязали тази публикация като любима", + "already-unfavourited": "Вече сте премахнали тази публикация от любимите си", + "cant-ban-other-admins": "Не можете да блокирате другите администратори!", + "invalid-image-type": "Грешен тип на изображение. Позволените типове са: %1", + "invalid-image-extension": "Грешно разширение на изображението", + "invalid-file-type": "Грешен тип на файл. Позволените типове са: %1", + "group-name-too-short": "Името на групата е твърде кратко", + "group-already-exists": "Вече съществува такава група", + "group-name-change-not-allowed": "Промяната на името на групата не е разрешено", + "group-already-member": "Вече сте част от тази група", + "group-needs-owner": "Тази група се нуждае от поне един собственик", + "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 символ(а).", + "cant-chat-with-yourself": "Не можете да пишете чат съобщение на себе си!", + "chat-restricted": "Този потребител е ограничил чат съобщенията до себе си. Той трябва първо да Ви последва, преди да можете да си пишете с него.", + "too-many-messages": "Изпратили сте твърде много съобщения. Моля, изчакайте малко.", + "reputation-system-disabled": "Системата за репутация е изключена.", + "downvoting-disabled": "Отрицателното гласуване е изключено", + "not-enough-reputation-to-downvote": "Нямате достатъчно репутация, за да гласувате отрицателно за тази публикация", + "not-enough-reputation-to-flag": "Нямате достатъчно репутация, за да докладвате тази публикация", + "reload-failed": "NodeBB срещна проблем при презареждането: „%1“. NodeBB ще продължи да поддържа съществуващите клиентски ресурси, но Вие трябва да отмените последните си действия преди презареждането.", + "registration-error": "Грешка при регистрацията", + "parse-error": "Нещо се обърка при прочитането на отговора на сървъра", + "wrong-login-type-email": "Моля, използвайте е-пощата си, за да влезете", + "wrong-login-type-username": "Моля, използвайте потребителското си име, за да влезете" +} \ No newline at end of file diff --git a/public/language/bg/global.json b/public/language/bg/global.json new file mode 100644 index 0000000000..a0b9ca9e26 --- /dev/null +++ b/public/language/bg/global.json @@ -0,0 +1,81 @@ +{ + "home": "Начало", + "search": "Търсене", + "buttons.close": "Затваряне", + "403.title": "Достъпът е отказан", + "403.message": "Изглежда сте посетили страница, до която нямате достъп.", + "403.login": "Може би трябва да опитате да влезете?", + "404.title": "Не е открита", + "404.message": "Изглежда сте се опитали да посетите страница, която не съществува. Върнете се към началната страница.", + "500.title": "Вътрешна грешка.", + "500.message": "Опа! Изглежда нещо се обърка!", + "register": "Регистриране", + "login": "Влизане", + "please_log_in": "Моля, влезте", + "logout": "Изход", + "posting_restriction_info": "Публикуването в момента е позволено само за регистрираните потребители. Натиснете тук, за да влезете.", + "welcome_back": "Добре дошли отново", + "you_have_successfully_logged_in": "Вие влязохте успешно", + "save_changes": "Запазване на промените", + "close": "Затваряне", + "pagination": "Страници", + "pagination.out_of": "%1 от %2", + "pagination.enter_index": "Въведете номер", + "header.admin": "Администратор", + "header.recent": "Скорошни", + "header.unread": "Непрочетени", + "header.tags": "Етикети", + "header.popular": "Популярни", + "header.users": "Потребители", + "header.groups": "Групи", + "header.chats": "Чатове", + "header.notifications": "Известия", + "header.search": "Търсене", + "header.profile": "Профил", + "notifications.loading": "Зареждане на известията", + "chats.loading": "Зареждане на чатовете", + "motd.welcome": "Добре дошли в NodeBB, системата за дискусии на бъдещето.", + "previouspage": "Предишна страница", + "nextpage": "Следваща страница", + "alert.success": "Готово", + "alert.error": "Грешка", + "alert.banned": "Блокиран", + "alert.banned.message": "Вие току-що бяхте блокиран. Сега ще излезете от системата.", + "alert.unfollow": "Вие вече не следвате %1!", + "alert.follow": "Вие следвате %1!", + "online": "На линия", + "users": "Потребители", + "topics": "Теми", + "posts": "Публикации", + "views": "Преглеждания", + "reputation": "Репутация", + "read_more": "още", + "posted_ago_by_guest": "публикувано %1 от гост", + "posted_ago_by": "публикувано %1 от %2", + "posted_ago": "публикувано %1", + "posted_in_ago_by_guest": "публикувано в %1 %2 от гост", + "posted_in_ago_by": "публикувано в %1 %2 от %3", + "posted_in_ago": "публикувано в %1 %2", + "replied_ago": "отговори %1", + "user_posted_ago": "%1 публикува %2", + "guest_posted_ago": "гост публикува %1", + "last_edited_by_ago": "последно редактирано от %1 %2", + "norecentposts": "Няма скорошни публикации", + "norecenttopics": "Няма скорошни теми", + "recentposts": "Скорошни публикации", + "recentips": "Наскоро ползвани IP адреси", + "away": "Отсъстващ", + "dnd": "Отпочиващ", + "invisible": "Невидим", + "offline": "Извън линия", + "email": "Е-поща", + "language": "Език", + "guest": "Гост", + "guests": "Гости", + "updated.title": "Форумът е актуализиран", + "updated.message": "Този форум току-що беше актуализиран до най-новата версия. Натиснете тук, за да опресните страницата.", + "privacy": "Поверителност", + "follow": "Следване", + "unfollow": "Прекратяване на следването", + "delete_all": "Изтриване на всичко" +} \ No newline at end of file diff --git a/public/language/bg/groups.json b/public/language/bg/groups.json new file mode 100644 index 0000000000..747b79d8a0 --- /dev/null +++ b/public/language/bg/groups.json @@ -0,0 +1,36 @@ +{ + "groups": "Групи", + "view_group": "Преглед на групата", + "owner": "Собственик на групата", + "new_group": "Създаване на нова група", + "no_groups_found": "Няма групи", + "pending.accept": "Приемане", + "pending.reject": "Отхвърляне", + "cover-instructions": "Плъзнете снимка, наместете я в предпочитаната позиция и натистнете Запазване", + "cover-change": "Промяна", + "cover-save": "Запазване", + "cover-saving": "Запазване", + "details.title": "Подробности за групата", + "details.members": "Списък на членовете", + "details.pending": "Кандидатстващи членове", + "details.has_no_posts": "Членовете на тази група не са публикували нищо.", + "details.latest_posts": "Скорошни публикации", + "details.private": "Частна", + "details.grant": "Даване/отнемане на собственост", + "details.kick": "Изгонване", + "details.owner_options": "Администрация на групата", + "details.group_name": "Име на групата", + "details.member_count": "Брой на членовете", + "details.creation_date": "Дата на създаване", + "details.description": "Описание", + "details.badge_preview": "Преглед на емблемата", + "details.change_icon": "Промяна на иконката", + "details.change_colour": "Промяна на цвета", + "details.badge_text": "Текст на емблемата", + "details.userTitleEnabled": "Показване на емблемата", + "details.private_help": "Ако е включено, присъединяването към група изисква одобрението на собственика ѝ", + "details.hidden": "Скрита", + "details.hidden_help": "Ако е включено, тази група няма да бъде извеждана в списъка от групи и потребителите ще трябва да бъдат поканени лично", + "event.updated": "Подробностите за групата бяха обновени", + "event.deleted": "Групата „%1“ беше изтрита" +} \ No newline at end of file diff --git a/public/language/bg/language.json b/public/language/bg/language.json new file mode 100644 index 0000000000..31a3b8055b --- /dev/null +++ b/public/language/bg/language.json @@ -0,0 +1,5 @@ +{ + "name": "Български", + "code": "bg", + "dir": "ltr" +} \ No newline at end of file diff --git a/public/language/bg/login.json b/public/language/bg/login.json new file mode 100644 index 0000000000..f8340495af --- /dev/null +++ b/public/language/bg/login.json @@ -0,0 +1,11 @@ +{ + "username-email": "Потребителско име / е-поща", + "username": "Потребителско име", + "email": "Е-поща", + "remember_me": "Запомнете ме?", + "forgot_password": "Забравена парола?", + "alternative_logins": "Други начини за влизане", + "failed_login_attempt": "Неуспешно влизане. Моля, опитайте отново.", + "login_successful": "Вие влязохте успешно!", + "dont_have_account": "Нямате акаунт?" +} \ No newline at end of file diff --git a/public/language/bg/modules.json b/public/language/bg/modules.json new file mode 100644 index 0000000000..d151330bad --- /dev/null +++ b/public/language/bg/modules.json @@ -0,0 +1,26 @@ +{ + "chat.chatting_with": "Чат с ", + "chat.placeholder": "Въведете чат съобщението тук и натиснете Ентер за изпращане", + "chat.send": "Изпращане", + "chat.no_active": "Нямате текущи чатове.", + "chat.user_typing": "%1 пише...", + "chat.user_has_messaged_you": "%1 Ви написа съобщение.", + "chat.see_all": "Вижте всички чатове", + "chat.no-messages": "Моля, изберете получател, за да видите историята на чат съобщенията", + "chat.recent-chats": "Скорошни чатове", + "chat.contacts": "Контакти", + "chat.message-history": "История на съобщенията", + "chat.pop-out": "Отделяне на чата в прозорец", + "chat.maximize": "Уголемяване", + "chat.seven_days": "7 дни", + "chat.thirty_days": "30 дни", + "chat.three_months": "3 месеца", + "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.toggle_dropdown": "Превключване на падащото меню" +} \ No newline at end of file diff --git a/public/language/bg/notifications.json b/public/language/bg/notifications.json new file mode 100644 index 0000000000..c646a60206 --- /dev/null +++ b/public/language/bg/notifications.json @@ -0,0 +1,27 @@ +{ + "title": "Известия", + "no_notifs": "Нямате нови известия", + "see_all": "Вижте всички известия", + "mark_all_read": "Отбелязване на всички известия като прочетени", + "back_to_home": "Назад към %1", + "outgoing_link": "Външна връзка", + "outgoing_link_message": "Вие напускате %1.", + "continue_to": "Продължаване към %1", + "return_to": "Връщане към %1", + "new_notification": "Ново известие", + "you_have_unread_notifications": "Имате непрочетени известия", + "new_message_from": "Ново съобщение от %1", + "upvoted_your_post_in": "%1 гласува положително за Ваша публикация в %2.", + "moved_your_post": "%1 премести Ваша публикация.", + "moved_your_topic": "%1 премести Ваша тема.", + "favourited_your_post_in": "%1 отбеляза Ваша публикация в %2 като любима.", + "user_flagged_post_in": "%1 докладва Ваша публикация в %2", + "user_posted_to": "%1 публикува отговор на: %2", + "user_posted_topic": "%1 публикува нова тема: %2", + "user_mentioned_you_in": "%1 Ви спомена в %2", + "user_started_following_you": "%1 започна да Ви следва.", + "email-confirmed": "Е-пощата беше потвърдена", + "email-confirmed-message": "Благодарим Ви, че потвърдихте е-пощата си. Акаунтът Ви е вече напълно активиран.", + "email-confirm-error-message": "Възникна проблем при потвърждаването на е-пощата Ви. Може кодът да е грешен или давността му да е изтекла.", + "email-confirm-sent": "Изпратено е е-писмо за потвърждение." +} \ No newline at end of file diff --git a/public/language/bg/pages.json b/public/language/bg/pages.json new file mode 100644 index 0000000000..d0d9c7ec82 --- /dev/null +++ b/public/language/bg/pages.json @@ -0,0 +1,21 @@ +{ + "home": "Начало", + "unread": "Непрочетени теми", + "popular": "Популярни теми", + "recent": "Скорошни теми", + "users": "Регистрирани потребители", + "notifications": "Известия", + "tags": "Етикети", + "tag": "Теми, отбелязани като „%1“", + "user.edit": "Редактиране на „%1“", + "user.following": "Хора, които %1 следва", + "user.followers": "Хора, които следват %1", + "user.posts": "Публикации от %1", + "user.topics": "Теми, създадени от %1", + "user.groups": "Групите на %1", + "user.favourites": "Любимите публикации на %1", + "user.settings": "Настройки на потребителя", + "user.watched": "Теми, следени от %1", + "maintenance.text": "%1 в момента е в профилактика. Моля, върнете се по-късно.", + "maintenance.messageIntro": "В допълнение, администраторът е оставил това съобщение:" +} \ No newline at end of file diff --git a/public/language/bg/recent.json b/public/language/bg/recent.json new file mode 100644 index 0000000000..a3029868b0 --- /dev/null +++ b/public/language/bg/recent.json @@ -0,0 +1,19 @@ +{ + "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 нови публикации.", + "click-here-to-reload": "Натиснете тук, за да презаредите." +} \ No newline at end of file diff --git a/public/language/bg/register.json b/public/language/bg/register.json new file mode 100644 index 0000000000..3f83b192e3 --- /dev/null +++ b/public/language/bg/register.json @@ -0,0 +1,18 @@ +{ + "register": "Регистрация", + "help.email": "По подразбиране, Вашата е-поща ще бъде скрита за останалите.", + "help.username_restrictions": "Уникално потребителско име с дължина между %1 и %2 символа. Другите ще могат да Ви споменават чрез @потребител.", + "help.minimum_password_length": "Дължината на паролата Ви трябва да е поне %1 символа.", + "email_address": "Е-поща", + "email_address_placeholder": "Въведете адрес на е-поща", + "username": "Потребителско име", + "username_placeholder": "Въведете потребителско име", + "password": "Парола", + "password_placeholder": "Въведете парола", + "confirm_password": "Потвърдете паролата", + "confirm_password_placeholder": "Потвърдете паролата", + "register_now_button": "Регистриране", + "alternative_registration": "Друг начин за регистриране", + "terms_of_use": "Условия за ползване", + "agree_to_terms_of_use": "Съгласен съм с условията за ползване" +} \ No newline at end of file diff --git a/public/language/bg/reset_password.json b/public/language/bg/reset_password.json new file mode 100644 index 0000000000..11d30c6fda --- /dev/null +++ b/public/language/bg/reset_password.json @@ -0,0 +1,17 @@ +{ + "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": "Информацията за подновяване на паролата беше изпратена", + "invalid_email": "Грешна е-поща / е-пощата не съществува!", + "password_too_short": "Паролата е твърде кратка. Моля, изберете друга парола.", + "passwords_do_not_match": "Двете пароли, които въведохте, са различни.", + "password_expired": "Паролата Ви е с изтекла давност. Моля, изберете нова парола" +} \ No newline at end of file diff --git a/public/language/bg/search.json b/public/language/bg/search.json new file mode 100644 index 0000000000..9055fb207e --- /dev/null +++ b/public/language/bg/search.json @@ -0,0 +1,40 @@ +{ + "results_matching": "%1 резултат(а), отговарящи на „%2“, (%3 секунди)", + "no-matches": "Няма съвпадения", + "advanced-search": "Разширено търсене", + "in": "В", + "titles": "Заглавия", + "titles-posts": "Заглавия и публикации", + "posted-by": "Публикувано от", + "in-categories": "В категории", + "search-child-categories": "Претърсване на подкатегориите", + "reply-count": "Брой на отговорите", + "at-least": "Поне", + "at-most": "Най-много", + "post-time": "Време на публикуване", + "newer-than": "По-нови от", + "older-than": "По-стари от", + "any-date": "Която и да е дата", + "yesterday": "Вчера", + "one-week": "Една седмица", + "two-weeks": "Две седмици", + "one-month": "Един месец", + "three-months": "Три месеца", + "six-months": "Шест месеца", + "one-year": "Една година", + "sort-by": "Подреждане по", + "last-reply-time": "Време на последния отговор", + "topic-title": "Заглавие на темата", + "number-of-replies": "Брой на отговорите", + "number-of-views": "Брой на преглежданията", + "topic-start-date": "Начална дата на темата", + "username": "Потребителско име", + "category": "Категория", + "descending": "В низходящ ред", + "ascending": "Във възходящ ред", + "save-preferences": "Запазване на предпочитанията", + "clear-preferences": "Изчистване на предпочитанията", + "search-preferences-saved": "Предпочитанията за търсене бяха запазени", + "search-preferences-cleared": "Предпочитанията за търсене бяха изчистени", + "show-results-as": "Показване на резултатите като" +} \ No newline at end of file diff --git a/public/language/bg/success.json b/public/language/bg/success.json new file mode 100644 index 0000000000..b7cc15d56f --- /dev/null +++ b/public/language/bg/success.json @@ -0,0 +1,6 @@ +{ + "success": "Готово", + "topic-post": "Вие публикувахте успешно.", + "authentication-successful": "Успешно удостоверяване", + "settings-saved": "Настройките са записани!" +} \ No newline at end of file diff --git a/public/language/bg/tags.json b/public/language/bg/tags.json new file mode 100644 index 0000000000..d421e5cd7d --- /dev/null +++ b/public/language/bg/tags.json @@ -0,0 +1,7 @@ +{ + "no_tag_topics": "Няма теми с този етикет.", + "tags": "Етикети", + "enter_tags_here": "Въведете етикетите тук, всеки може да е с дължина между %1 и %2 символа.", + "enter_tags_here_short": "Въведете етикети...", + "no_tags": "Все още няма етикети." +} \ No newline at end of file diff --git a/public/language/bg/topic.json b/public/language/bg/topic.json new file mode 100644 index 0000000000..03062d7e70 --- /dev/null +++ b/public/language/bg/topic.json @@ -0,0 +1,99 @@ +{ + "topic": "Тема", + "topic_id": "Идентификатора на темата", + "topic_id_placeholder": "Въведете идентификатор на темата", + "no_topics_found": "Няма открити теми!", + "no_posts_found": "Няма открити публикации!", + "post_is_deleted": "Тази публикация е изтрита!", + "profile": "Профил", + "posted_by": "Публикувано от %1", + "posted_by_guest": "Публикувано от гост", + "chat": "Чат", + "notify_me": "Получавайте известия за новите отговори в тази тема", + "quote": "Цитат", + "reply": "Отговор", + "guest-login-reply": "Влезте, за да отговорите", + "edit": "Редактиране", + "delete": "Изтриване", + "purge": "Изчистване", + "restore": "Възстановяване", + "move": "Преместване", + "fork": "Разделяне", + "link": "Връзка", + "share": "Споделяне", + "tools": "Инструменти", + "flag": "Докладване", + "locked": "Заключена", + "bookmark_instructions": "Натиснете, за да се върнете на последната си позиция или затворете, за да отхвърлите.", + "flag_title": "Докладване на тази публикация до модератор", + "flag_confirm": "Сигурни ли сте, че искате да докладвате тази публикация?", + "flag_success": "Тази публикация е била докладвана до модератор.", + "deleted_message": "Тази тема е била изтрита. Само потребители с права за управление на темите могат да я видят.", + "following_topic.message": "Вече ще получавате известия когато някой публикува коментар в тази тема.", + "not_following_topic.message": "Вече няма да получавате известия за тази тема.", + "login_to_subscribe": "Моля, регистрирайте се или влезте, за да се абонирате за тази тема.", + "markAsUnreadForAll.success": "Темата е отбелязана като непрочетена за всички.", + "watch": "Наблюдаване", + "unwatch": "Спиране на наблюдаването", + "watch.title": "Получавайте известия за новите отговори в тази тема", + "unwatch.title": "Спрете да наблюдавате тази тема", + "share_this_post": "Споделете тази публикация", + "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.fork": "Разделяне на темата", + "thread_tools.delete": "Изтриване на темата", + "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": "Изключените категории са засивени", + "confirm_move": "Преместване", + "confirm_fork": "Разделяне", + "favourite": "Любима", + "favourites": "Любими", + "favourites.has_no_favourites": "Нямате любими, отбележете няколко публикации, за да ги видите тук!", + "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_success": "Темата е разделена успешно! Натиснете тук, за да преминете към отделената тема.", + "composer.title_placeholder": "Въведете заглавието на темата си тук...", + "composer.handle_placeholder": "Име", + "composer.discard": "Отхвърляне", + "composer.submit": "Публикуване", + "composer.replying_to": "Отговор на %1", + "composer.new_topic": "Нова тема", + "composer.uploading": "качване...", + "composer.thumb_url_label": "Поставете адреса на иконка за темата", + "composer.thumb_title": "Добавете иконка към тази тема", + "composer.thumb_url_placeholder": "http://example.com/thumb.png", + "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": "Подреждане по", + "oldest_to_newest": "Първо най-старите", + "newest_to_oldest": "Първо най-новите", + "most_votes": "Най-много гласове", + "most_posts": "Най-много публикации" +} \ No newline at end of file diff --git a/public/language/bg/unread.json b/public/language/bg/unread.json new file mode 100644 index 0000000000..cff5d865ba --- /dev/null +++ b/public/language/bg/unread.json @@ -0,0 +1,9 @@ +{ + "title": "Непрочетени", + "no_unread_topics": "Няма непрочетени теми.", + "load_more": "Зареждане на още", + "mark_as_read": "Отбелязване като прочетени", + "selected": "Избраните", + "all": "Всички", + "topics_marked_as_read.success": "Темите бяха отбелязани като прочетени!" +} \ No newline at end of file diff --git a/public/language/bg/user.json b/public/language/bg/user.json new file mode 100644 index 0000000000..f6b68145f1 --- /dev/null +++ b/public/language/bg/user.json @@ -0,0 +1,83 @@ +{ + "banned": "Блокиран", + "offline": "Извън линия", + "username": "Потребителско име", + "joindate": "Дата на присъединяване", + "postcount": "Брой публикации", + "email": "Е-поща", + "confirm_email": "Потвърдете е-пощата", + "delete_account": "Изтриване на акаунта", + "delete_account_confirm": "Сигурни ли сте, че искате да изтриете акаунта си?
Това действие е необратимо и няма да можете да възстановите нищо от данните си

Въведете потребителското си име, за да потвърдите, че искате да унищожите този акаунт.", + "fullname": "Цяло име", + "website": "Уеб сайт", + "location": "Местоположение", + "age": "Възраст", + "joined": "Присъединил се", + "lastonline": "Последно на линия", + "profile": "Профил", + "profile_views": "Преглеждания на профила", + "reputation": "Репутация", + "favourites": "Любими", + "watched": "Наблюдавани", + "followers": "Последователи", + "following": "Следва", + "signature": "Подпис", + "gravatar": "Граватар", + "birthday": "Рождена дата", + "chat": "Чат", + "follow": "Следване", + "unfollow": "Спиране на следването", + "more": "Още", + "profile_update_success": "Профилът беше обновен успешно!", + "change_picture": "Промяна на снимката", + "edit": "Редактиране", + "uploaded_picture": "Качена снимка", + "upload_new_picture": "Качване на нова снимка", + "upload_new_picture_from_url": "Качване на нова снимка от адрес", + "current_password": "Текуща парола", + "change_password": "Промяна на паролата", + "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": "Потвърдете паролата", + "password": "Парола", + "username_taken_workaround": "Потребителското име, което искате, е заето и затова ние го променихме леко. Вие ще се наричате %1", + "upload_picture": "Качване на снимка", + "upload_a_picture": "Качване на снимка", + "image_spec": "Можете да качвате само PNG, JPG, или GIF файлове", + "settings": "Настройки", + "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": "Този потребител не е наблюдавал нито една тема все още.", + "email_hidden": "Е-пощата е скрита", + "hidden": "скрито", + "paginate_description": "Разделяне на темите и публикациите на страници, вместо да се превърта безкрайно", + "topics_per_page": "Теми на страница", + "posts_per_page": "Публикации на страница", + "notification_sounds": "Изпълняване на звук, когато получите известие", + "browsing": "Настройки за страниците", + "open_links_in_new_tab": "Отваряне на външните връзки в нов подпрозорец", + "enable_topic_searching": "Включване на търсенето в темите", + "topic_search_help": "Ако е включено, търсенето в темата ще замени стандартното поведение на браузъра при търсене в страницата и ще Ви позволи да претърсвате цялата тема, а не само това, което се вижда на екрана", + "follow_topics_you_reply_to": "Следване на темите, на които отговаряте", + "follow_topics_you_create": "Следване на темите, които създавате", + "grouptitle": "Изберете заглавието на групата, което искате да се показва", + "no-group-title": "Няма заглавие на група" +} \ No newline at end of file diff --git a/public/language/bg/users.json b/public/language/bg/users.json new file mode 100644 index 0000000000..ec375fb01e --- /dev/null +++ b/public/language/bg/users.json @@ -0,0 +1,12 @@ +{ + "latest_users": "Последни потребители", + "top_posters": "С най-много публикации", + "most_reputation": "С най-много репутация", + "search": "Търсене", + "enter_username": "Въведете потребителско име, което да потърсите", + "load_more": "Зареждане на още", + "users-found-search-took": "Намерени са %1 потребител(и)! Търсенето отне %2 секунди.", + "filter-by": "Филтриране", + "online-only": "Само тези на линия", + "picture-only": "Само със снимка" +} \ No newline at end of file diff --git a/public/language/bn/category.json b/public/language/bn/category.json index b706e85080..52a6c76853 100644 --- a/public/language/bn/category.json +++ b/public/language/bn/category.json @@ -5,5 +5,8 @@ "browsing": "ব্রাউজিং", "no_replies": "কোন রিপ্লাই নেই", "share_this_category": "এই বিভাগটি অন্যের সাথে ভাগাভাগি করুন", - "ignore": "উপেক্ষা করুন" + "watch": "Watch", + "ignore": "উপেক্ষা করুন", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/bn/error.json b/public/language/bn/error.json index 71ff379ba6..40b8dc44d7 100644 --- a/public/language/bn/error.json +++ b/public/language/bn/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "খুব ছোট ইউজারনেম", "username-too-long": "ইউজারনেম বড় হয়ে গিয়েছে", "user-banned": "ব্যবহারকারী নিষিদ্ধ", - "user-too-new": "দুঃখিত! প্রথম পোষ্ট করার জন্য আপনাকে %1 সেকেন্ড অপেক্ষা করতে হবে।", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "বিভাগটি খুজে পাওয়া যায় নি", "no-topic": "এই টপিক নেই", "no-post": "এই পোষ্ট নেই", @@ -35,17 +36,17 @@ "no-emailers-configured": "কোন ইমেল প্লাগইন লোড করা নেই, কাজেই টেস্ট মেইল পাঠানো সম্ভব হচ্ছে না", "category-disabled": "বিভাগটি নিষ্ক্রিয়", "topic-locked": "টপিক বন্ধ", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "আপলোড সম্পূর্ণ জন্য অনুগ্রহ করে অপেক্ষা করুন", - "content-too-short": "অনুগ্রহকরে অপেক্ষকৃত বড় পোষ্ট করুন। একটি পোষ্টে নূন্যতম %1 অক্ষর থাকতে হবে।", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "অনুগ্রহপূর্বক বড় শিরোনাম ব্যাবহার করুন। শিরোনামের জন্য নূন্যতম %1 অক্ষর ব্যাবহার করতে হবে।", - "title-too-long": "অনুগ্রহ করে সংক্ষিপ্ত শিরোনাম লিখুন। শিরোনাম %1 অক্ষরের বেশি হতে পারবে না।", - "too-many-posts": "আপনি প্রতি %1 সেকেন্ডে একবার পোষ্ট করতে পারবেন। পরবর্তী পোষ্ট করার জন্য অপেক্ষা করুন। ", - "too-many-posts-newbie": "নতুন সদস্য হিসাবে %2 সন্মানণা পাওয়া পর্যন্ত আপনি প্রতি %1 সেকেন্ডে একবার পোষ্ট করতে পারবেন। পরবর্তী পোষ্ট করার জন্য অপেক্ষা করুন। ", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "সর্বোচ্চ গৃহীত ফাইলসাইজ হচ্ছে %1 kb - অনুগ্রহপূর্বক ছোট ফাইল আপলোড করুন", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "আপনি নিজের পোস্টে ভোট দিতে পারবেন না।", "already-favourited": "আপনি ইতিমধ্যে এই পোষ্টটি পছন্দের তালিকায় যোগ করেছেন", "already-unfavourited": "আপনি ইতিমধ্যে এই পোষ্টটি আপনার পছন্দের তালিকা থেকে সরিয়ে ফেলেছেন", @@ -62,10 +63,11 @@ "post-already-restored": "এই পোষ্টটি ইতিমধ্যে পুনরোদ্ধার করা হয়েছে", "topic-already-deleted": "এই টপিকটি ইতিমধ্যে ডিলিট করা হয়েছে", "topic-already-restored": "এই টপিকটি ইতিমধ্যে পুনরোদ্ধার করা হয়েছে", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "টপিক থাম্বনেল নিষ্ক্রিয় করা। ", "invalid-file": "ভুল ফাইল", "uploads-are-disabled": "আপলোড নিষ্ক্রিয় করা", - "signature-too-long": "দুঃখিত, আপনার সাক্ষর %1 অক্ষরের বেশী হতে পারবে না। ", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "আপনি নিজের সাথে চ্যাট করতে পারবেন না!", "chat-restricted": "এই সদস্য তার বার্তালাপ সংরক্ষিত রেখেছেন। এই সদস্য আপনাকে ফলো করার পরই কেবলমাত্র আপনি তার সাথে চ্যাট করতে পারবেন", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/bn/groups.json b/public/language/bn/groups.json index 50e0a886dc..d87b5bc5d0 100644 --- a/public/language/bn/groups.json +++ b/public/language/bn/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/bn/modules.json b/public/language/bn/modules.json index 3dd01df610..eca6afb7cb 100644 --- a/public/language/bn/modules.json +++ b/public/language/bn/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "৭ দিন", "chat.thirty_days": "৩০ দিন", "chat.three_months": "৩ মাস", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 বলেছেন %2:", "composer.user_said": "%1 বলেছেনঃ", "composer.discard": "আপনি কি নিশ্চিত যে আপনি এই পোস্ট বাতিল করতে ইচ্ছুক?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/bn/pages.json b/public/language/bn/pages.json index 0da3b3b4a7..3226cc5431 100644 --- a/public/language/bn/pages.json +++ b/public/language/bn/pages.json @@ -5,7 +5,8 @@ "recent": "সাম্প্রতিক টপিক", "users": "নিবন্ধিত সদস্যগণ", "notifications": "বিজ্ঞপ্তি", - "tags": "\"%1\" এ ট্যগকৃত টপিকসমূহ", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "সম্পাদনা \"%1\"", "user.following": "%1 যাদের অনুসরণ করেন", "user.followers": "যারা %1 কে অনুসরণ করেন", diff --git a/public/language/bn/reset_password.json b/public/language/bn/reset_password.json index 5643e1c427..dd99dd4db3 100644 --- a/public/language/bn/reset_password.json +++ b/public/language/bn/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "পাসওয়ার্ড রিসেট মেইল পাঠানো হয়েছে", "invalid_email": "ভুল ইমেইল / ইমেইল ডেটাবেইজে নেই", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/bn/search.json b/public/language/bn/search.json index 8fef1aef5a..66f4c69a7e 100644 --- a/public/language/bn/search.json +++ b/public/language/bn/search.json @@ -1,8 +1,8 @@ { "results_matching": "\"%2\" এর সাথে মিলিয়ে %1 ফলাফল পাওয়া গেছে, ( %3 seconds সময় লেগেছে )", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/bn/user.json b/public/language/bn/user.json index b9acb70ad0..5107df1fa7 100644 --- a/public/language/bn/user.json +++ b/public/language/bn/user.json @@ -27,6 +27,7 @@ "chat": "বার্তালাপ", "follow": "অনুসরন করুন", "unfollow": "অনুসরন করা থেকে বিরত থাকুন", + "more": "More", "profile_update_success": "প্রোফাইল আপডেট সফল হয়েছে", "change_picture": "ছবি পরিবর্তন", "edit": "সম্পাদনা", @@ -47,7 +48,6 @@ "upload_picture": "ছবি আপলোড করুন", "upload_a_picture": "ছবি (একটি) আপলোড করুন", "image_spec": "আপনি কেবলমাত্র PNG, JPG অথবা GIF ফাইল আপলোড করতে পারবেন", - "max": "সর্বোচ্চ", "settings": "সেটিংস", "show_email": "আমার ইমেইল দেখাও", "show_fullname": "আমার সম্পূর্ণ নাম দেখাও", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "ইমেইল গোপন রাখা হয়েছে", "hidden": "গোপন করা হয়েছে", - "paginate_description": "ইনফাইনাইট স্ক্রলের বদলে টপিক এবং পোষ্টের জন্য পেজিনেশন ব্যাবহার করুন", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "প্রতি পেজে কতগুলো টপিক থাকবে", "posts_per_page": "প্রতি পেইজে কতগুলো পোষ্ট থাকবে", - "notification_sounds": "নতুন নোটিফিকেশনের জন্য নোটিফিকেশন সাউন্ড বাজাও।", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing সেটিংস", - "open_links_in_new_tab": "বাইরের URL গুলো নতুন ট্যাবে খোলা হবে ?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "In-Topic সার্চ সক্রীয় করো", - "topic_search_help": "যদি এনাবল করা হয়ে থাকে, In-topic সার্চিং ব্রাউজারের ডিফল্ট সার্চের বদলে পুরো টপিকজুড়ে সার্চ করার সুবিধা দিবে, যা কেবলমাত্র বর্তমান স্কৃণে দেখানো অংশের মধ্যে সীমাবদ্ধ থাকবে না। ", - "follow_topics_you_reply_to": "আপনার উত্তর দেয়া টপিকগুলো ফলো করুন", - "follow_topics_you_create": "আপনার তৈরীকরা টপিকসমূহ ফলো করুন", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/cs/category.json b/public/language/cs/category.json index 0d598e974a..6f156ea04b 100644 --- a/public/language/cs/category.json +++ b/public/language/cs/category.json @@ -5,5 +5,8 @@ "browsing": "prohlíží", "no_replies": "Nikdo ještě neodpověděl", "share_this_category": "Share this category", - "ignore": "Ignorovat" + "watch": "Watch", + "ignore": "Ignorovat", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/cs/error.json b/public/language/cs/error.json index fdfdb5f6d3..1a82707bd9 100644 --- a/public/language/cs/error.json +++ b/public/language/cs/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Uživatelské jméno je příliš krátké", "username-too-long": "Uživatelské jméno je příliš dlouhé", "user-banned": "Uživatel byl zakázán", - "user-too-new": "Pardon, ale je potřeba vyčkat %1 sekund, než-li budete moci vytvořit svůj první příspěvek.", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategorie neexistuje", "no-topic": "Téma neexistuje", "no-post": "Příspěvek neexistuje", @@ -35,17 +36,17 @@ "no-emailers-configured": "Protože není zaveden žádný emailový plugin, není možné odeslat testovací email.", "category-disabled": "Kategorie zakázána", "topic-locked": "Téma uzamčeno", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Vyčkejte, prosím, nežli se vše kompletně nahraje.", - "content-too-short": "Vložte, prosím, delší příspěvek. Příspěvky by měly obsahovat nejméně %1 znaků.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Vložte, prosím, delší titulek. Titulky by měly obsahovat nejméně %1 znaků.", - "title-too-long": "Vložte, prosím, kratší titulek. Titulky by neměly být delší, než-li %1 znaků.", - "too-many-posts": "Své příspěvky můžete odesílat po %1 sekundách - vyčkejte, prosím, před dalším odesláním", - "too-many-posts-newbie": "Jako nový uživatel můžete své příspěvky odesílat po %1 sekundách, dokud nedosáhnete %2 reputace - vyčkejte, prosím, před dalším odesláním", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximální povolená velikost souboru je %1 kbs - nahrávejte, prosím, menší soubory", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Nemůžete hlasovat pro svůj vlastní příspěvek", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "Tento příspěvek byl již obnoven", "topic-already-deleted": "Toto téma bylo již vymazáno", "topic-already-restored": "Toto téma bylo již obnoveno", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Neplatný soubor", "uploads-are-disabled": "Nahrávání je zakázáno", - "signature-too-long": "Pardon, ale váš podpis nemůže být delší, než-li %1 znaků.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nemůžete chatovat sami se sebou!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/cs/groups.json b/public/language/cs/groups.json index 06b86790ae..9a52faf4f7 100644 --- a/public/language/cs/groups.json +++ b/public/language/cs/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/cs/modules.json b/public/language/cs/modules.json index 800f56a62a..ebda99d83e 100644 --- a/public/language/cs/modules.json +++ b/public/language/cs/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 dní", "chat.thirty_days": "30 dní", "chat.three_months": "3 měsíce", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/cs/pages.json b/public/language/cs/pages.json index 1bcf760876..afe6d74248 100644 --- a/public/language/cs/pages.json +++ b/public/language/cs/pages.json @@ -5,7 +5,8 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/cs/reset_password.json b/public/language/cs/reset_password.json index ba87244661..566b746435 100644 --- a/public/language/cs/reset_password.json +++ b/public/language/cs/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Obnova hesla odeslána", "invalid_email": "Špatný email / Email neexistuje!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/cs/search.json b/public/language/cs/search.json index 9dad8b6eab..277c0a32bc 100644 --- a/public/language/cs/search.json +++ b/public/language/cs/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/cs/user.json b/public/language/cs/user.json index 46bf40cc1c..171ed20163 100644 --- a/public/language/cs/user.json +++ b/public/language/cs/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Follow", "unfollow": "Unfollow", + "more": "More", "profile_update_success": "Profil byl úspěšně aktualizován!", "change_picture": "Změnit obrázek", "edit": "Upravit", @@ -47,7 +48,6 @@ "upload_picture": "Nahrát obrázek", "upload_a_picture": "Nahrát obrázek", "image_spec": "You may only upload PNG, JPG, or GIF files", - "max": "max.", "settings": "Nastavení", "show_email": "Zobrazovat můj email v profilu", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Skrytý email", "hidden": "skrytý", - "paginate_description": "Paginate topics and posts instead of using infinite scroll.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Topics per Page", "posts_per_page": "Posts per Page", - "notification_sounds": "Play a sound when you receive a notification.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/de/category.json b/public/language/de/category.json index f6a13932c2..f073846d6d 100644 --- a/public/language/de/category.json +++ b/public/language/de/category.json @@ -5,5 +5,8 @@ "browsing": "Aktiv", "no_replies": "Niemand hat geantwortet", "share_this_category": "Teile diese Kategorie", - "ignore": "Ignorieren" + "watch": "Beobachten", + "ignore": "Ignorieren", + "watch.message": "Du beobachtest jetzt Änderungen in dieser Kategorie", + "ignore.message": "Du ignorierst jetzt Änderungen in dieser Kategorie" } \ No newline at end of file diff --git a/public/language/de/error.json b/public/language/de/error.json index 821060aa25..3c1248097a 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -18,13 +18,14 @@ "username-taken": "Der Benutzername ist bereits vergeben", "email-taken": "Die E-Mail-Adresse ist bereits vergeben", "email-not-confirmed": "Deine E-Mail wurde noch nicht bestätigt. Bitte klicke hier, um deine E-Mail zu bestätigen.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "Deine E-Mail wurde noch nicht bestätigt. Bitte klicke hier, um deine E-Mail zu bestätigen.", "no-email-to-confirm": "Dieses Forum setzt E-Mail-Bestätigung voraus, bitte klick hier um eine E-Mail-Adresse einzugeben", "email-confirm-failed": "Wir konnten deine E-Mail-Adresse nicht bestätigen, bitte versuch es später noch einmal", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Benutzername ist zu kurz", "username-too-long": "Der Benutzername ist zu lang", "user-banned": "Der Benutzer ist gesperrt", - "user-too-new": "Tut uns leid, du musst %1 Sekunden warten, bevor du deinen ersten Beitrag verfassen kannst!", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Die Kategorie existiert nicht", "no-topic": "Das Thema existiert nicht", "no-post": "Der Beitrag existiert nicht", @@ -35,17 +36,17 @@ "no-emailers-configured": "Es wurde keine E-Mail-Plugins geladen, weshalb eine Test-E-Mail nicht gesendet werden konnte.", "category-disabled": "Kategorie ist deaktiviert", "topic-locked": "Thema ist gesperrt", - "post-edit-duration-expired": "Du darfst Beiträge lediglich innerhalb von %1 Sekunden nach dem erstellen editieren", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Bitte warte bis der Vorgang abgeschlossen ist.", - "content-too-short": "Bitte gib einen längeren Beitrag ein. Beiträge sollten mindestens %1 Zeichen enthalten.", - "content-too-long": "Bitte schreibe einen kürzeren Beitrag. Beiträge können nicht mehr als %1 Zeichen enthalten.", - "title-too-short": "Bitte gib einen längeren Titel ein. Titel sollten mindestens %1 Zeichen enthalten.", - "title-too-long": "Der Titel darf maximal %1 Zeichen enthalten.", - "too-many-posts": "Du kannst maximal alle %1 Sekunden einen Beitrag erstellen - bitte warte, bevor du einen neuen Beitrag erstellst", - "too-many-posts-newbie": "Als neuer Benutzer kannst du nur alle %1 Sekunden einen Beitrag verfassen, bis du %2 Reputationspunkte hast - Bitte warte etwas, bevor du erneut einen Beitrag verfasst", - "tag-too-short": "Bitte gib ein längeres Stichwort an. Stichwörter sollten aus mindestens %1 Zeichen bestehen.", - "tag-too-long": "Bitte gib ein kürzeres Stichwort ein. Stichwörter können nicht länger als %1 Zeichen sein.", - "file-too-big": "Die maximale Dateigröße beträgt %1 kB - bitte lade eine kleinere Datei hoch", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Du kannst deinen eigenen Beitrag nicht bewerten", "already-favourited": "Dieser Beitrag ist bereits in deinen Favoriten enthalten", "already-unfavourited": "Du hast diesen Beitrag bereits aus deinen Favoriten entfernt", @@ -62,10 +63,11 @@ "post-already-restored": "Dieser Beitrag ist bereits wiederhergestellt worden", "topic-already-deleted": "Dieses Thema ist bereits gelöscht worden", "topic-already-restored": "Dieses Thema ist bereits wiederhergestellt worden", + "cant-purge-main-post": "Du kannst den Hauptbeitrag nicht löschen, bitte lösche stattdessen das Thema", "topic-thumbnails-are-disabled": "Vorschaubilder für Themen sind deaktiviert", "invalid-file": "Datei ungültig", "uploads-are-disabled": "Uploads sind deaktiviert", - "signature-too-long": "Entschuldigung, deine Signatur darf maximal %1 Zeichen enthalten.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Du kannst nicht mit dir selber chatten!", "chat-restricted": "Dieser Benutzer hat seine Chatfunktion eingeschränkt. Du kannst nur mit diesem Benutzer chatten, wenn er dir folgt.", "too-many-messages": "Du hast zu viele Nachrichten versandt, bitte warte eine Weile.", diff --git a/public/language/de/groups.json b/public/language/de/groups.json index 52fa359f53..c2328959aa 100644 --- a/public/language/de/groups.json +++ b/public/language/de/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Gruppenadministration", "details.group_name": "Gruppenname", + "details.member_count": "Mitgliederanzahl", + "details.creation_date": "Erstelldatum", "details.description": "Beschreibung", "details.badge_preview": "Abzeichenvorschau", "details.change_icon": "Symbol ändern", diff --git a/public/language/de/modules.json b/public/language/de/modules.json index d1c1e61f4f..d0287d790c 100644 --- a/public/language/de/modules.json +++ b/public/language/de/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Tage", "chat.thirty_days": "30 Tage", "chat.three_months": "3 Monate", + "composer.compose": "Verfassen", + "composer.show_preview": "Vorschau anzeigen", + "composer.hide_preview": "Vorschau ausblenden", "composer.user_said_in": "%1 sagte in %2:", "composer.user_said": "%1 sagte:", - "composer.discard": "Bist du sicher, dass du diesen Post verwerfen möchtest?", - "composer.submit_and_lock": "Submit and Lock" + "composer.discard": "Bist du sicher, dass du diesen Beitrag verwerfen möchtest?", + "composer.submit_and_lock": "Einreichen und Sperren", + "composer.toggle_dropdown": "Menu aus-/einblenden" } \ No newline at end of file diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 1fee71d136..781ccb7962 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -5,7 +5,8 @@ "recent": "Neueste Themen", "users": "Registrierte User", "notifications": "Benachrichtigungen", - "tags": "Themen markiert unter \"%1\"", + "tags": "Markierungen", + "tag": "Themen markiert unter \"%1\"", "user.edit": "Bearbeite \"%1\"", "user.following": "Nutzer, die %1 folgt", "user.followers": "Nutzer, die %1 folgen", @@ -14,7 +15,7 @@ "user.groups": "%1's Gruppen", "user.favourites": "Von %1 favorisierte Beiträge", "user.settings": "Benutzer-Einstellungen", - "user.watched": "Topics watched by %1", + "user.watched": "Themen angeschaut von %1", "maintenance.text": "%1 befindet sich derzeit in der Wartung. Bitte komm später wieder.", "maintenance.messageIntro": "Zusätzlich hat der Administrator diese Nachricht hinterlassen:" } \ No newline at end of file diff --git a/public/language/de/reset_password.json b/public/language/de/reset_password.json index 02d9329f7d..3428ad3375 100644 --- a/public/language/de/reset_password.json +++ b/public/language/de/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Passwortzurücksetzung beantragt.", "invalid_email": "Ungültige E-Mail / Adresse existiert nicht!", "password_too_short": "Das eingegebene Passwort ist zu kurz, bitte wähle ein anderes Passwort.", - "passwords_do_not_match": "Die eingegebenen Passwörter stimmen nicht überein." + "passwords_do_not_match": "Die eingegebenen Passwörter stimmen nicht überein.", + "password_expired": "Dein Passwort ist abgelaufen, bitte wähle ein neues Passwort" } \ No newline at end of file diff --git a/public/language/de/search.json b/public/language/de/search.json index b803315a9d..a8ac1251f0 100644 --- a/public/language/de/search.json +++ b/public/language/de/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 Ergebnis(se) stimmen mit \"%2\" überein, (%3 Sekunden)", "no-matches": "Keine Ergebnisse gefunden", + "advanced-search": "Erweiterte Suche", "in": "In", - "by": "Bei", "titles": "Titel", "titles-posts": "Titel und Beiträge", "posted-by": "Geschrieben von", diff --git a/public/language/de/tags.json b/public/language/de/tags.json index dc1c643266..0e0b1d3566 100644 --- a/public/language/de/tags.json +++ b/public/language/de/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Es gibt keine Themen mit diesem Stichwort.", "tags": "Stichwörter", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Gib hier Stichwörter ein. %1-%2 Zeichen. Drücke Enter nach jedem Stichwort", "enter_tags_here_short": "Gib Stichwörter ein...", "no_tags": "Es gibt bisher keine Stichwörter." } \ No newline at end of file diff --git a/public/language/de/topic.json b/public/language/de/topic.json index 7036cb823a..c4fe7732d8 100644 --- a/public/language/de/topic.json +++ b/public/language/de/topic.json @@ -15,7 +15,7 @@ "guest-login-reply": "Anmelden zum Antworten", "edit": "bearbeiten", "delete": "löschen", - "purge": "bereinigen", + "purge": "säubern", "restore": "Wiederherstellen", "move": "verschieben", "fork": "Aufspalten", @@ -51,12 +51,12 @@ "thread_tools.delete_confirm": "Bist du sicher, dass du dieses Thema löschen möchtest?", "thread_tools.restore": "Thema wiederherstellen", "thread_tools.restore_confirm": "Bist du sicher, dass du dieses Thema wiederherstellen möchtest?", - "thread_tools.purge": "Thema bereinigen", - "thread_tools.purge_confirm": "Bist du sicher, dass du dieses Thema bereinigen möchtest?", + "thread_tools.purge": "Thema säubern", + "thread_tools.purge_confirm": "Bist du sicher, dass du dieses Thema säubern möchtest?", "topic_move_success": "Thema wurde erfolgreich zu %1 verschoben.", "post_delete_confirm": "Sind Sie sicher, dass Sie diesen Beitrag löschen möchten?", "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?", + "post_purge_confirm": "Sind Sie sicher, das Sie diesen Beitrag säubern möchten?", "load_categories": "Kategorien laden", "disabled_categories_note": "Deaktivierte Kategorien sind ausgegraut.", "confirm_move": "Verschieben", diff --git a/public/language/de/user.json b/public/language/de/user.json index 5eaf971ab8..14772d4a2d 100644 --- a/public/language/de/user.json +++ b/public/language/de/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Folgen", "unfollow": "Nicht mehr folgen", + "more": "Mehr", "profile_update_success": "Profil erfolgreich aktualisiert!", "change_picture": "Profilbild ändern", "edit": "Ändern", @@ -47,7 +48,6 @@ "upload_picture": "Bild hochladen", "upload_a_picture": "Ein Bild hochladen", "image_spec": "Sie dürfen nur Dateien vom Typ PNG, JPG oder GIF hochladen", - "max": "max.", "settings": "Einstellungen", "show_email": "Zeige meine E-Mail Adresse an.", "show_fullname": "Zeige meinen kompletten Namen an", @@ -60,7 +60,7 @@ "digest_monthly": "Monatlich", "send_chat_notifications": "Sende eine E-Mail, wenn eine neue Chat-Nachricht eingeht und ich nicht online bin", "send_post_notifications": "Sende eine E-Mail wenn auf Themen die ich abonniert habe geantwortet wird", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Manche Einstellungsänderung benötigt ein aktualisieren. Drücke hier um die Seite neu zu laden.", "has_no_follower": "Dieser User hat noch keine Follower.", "follows_no_one": "Dieser User folgt noch niemandem :(", "has_no_posts": "Dieser Nutzer hat noch nichts gepostet.", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Diese Person beobachtet keine Themen", "email_hidden": "E-Mail Adresse versteckt", "hidden": "versteckt", - "paginate_description": "Themen und Beiträge auf Seiten aufteilen statt unendliches Scrollen verwenden.", + "paginate_description": "Themen und Beiträge in Seiten aufteilen, anstelle unendlich zu scrollen", "topics_per_page": "Themen pro Seite", "posts_per_page": "Beiträge pro Seite", - "notification_sounds": "Ton abspielen, wenn ich eine Benachrichtigung erhalte.", - "browsing": "Browser Einstellungen", - "open_links_in_new_tab": "Externe Links in neuem Tab öffnen?", + "notification_sounds": "Ton abspielen, wenn du eine Benachrichtigung erhältst", + "browsing": "Stöbereinstellungen", + "open_links_in_new_tab": "Ausgehende Links in neuem Tab öffnen", "enable_topic_searching": "Suchen innerhalb von Themen aktivieren", - "topic_search_help": "Falls aktiviert, wird die Suche im Thema das Standardsuchverhalten des Browsers überschreiben und es Ihnen erlauben, das ganze Thema statt dessen, was sich auf dem Bildschirm befindet, zu durchsuchen.", - "follow_topics_you_reply_to": "Folge Themen, auf die du antwortest.", - "follow_topics_you_create": "Folge Themen, die du erstellst.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Themen folgen, in denen auf dich geantwortet wird", + "follow_topics_you_create": "Themen folgen, die du erstellst", + "grouptitle": "Wähle den anzuzeigenden Gruppen Titel aus", + "no-group-title": "Kein Gruppentitel" } \ No newline at end of file diff --git a/public/language/el/category.json b/public/language/el/category.json index 73f3b51724..b49b18c0e2 100644 --- a/public/language/el/category.json +++ b/public/language/el/category.json @@ -5,5 +5,8 @@ "browsing": "περιηγούνται", "no_replies": "Κανείς δεν έχει απαντήσει", "share_this_category": "Μοιράσου αυτή την κατηγορία", - "ignore": "Αγνόηση" + "watch": "Watch", + "ignore": "Αγνόηση", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/el/error.json b/public/language/el/error.json index 2d00c366d4..5749bd7156 100644 --- a/public/language/el/error.json +++ b/public/language/el/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Το όνομα χρήστη είναι πολύ μικρό", "username-too-long": "Το όνομα χρήστη είναι πολύ μεγάλο", "user-banned": "Ο Χρήστης είναι αποκλεισμένος/η", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "Δεν έχουν φορτωθεί email plugins, οποτε το δοκιμαστικό email δεν μπορεί να σταλθεί", "category-disabled": "Η κατηγορία έχει απενεργοποιηθεί", "topic-locked": "Το θέμα έχει κλειδωθεί", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Παρακαλώ περίμενε να τελειώσει το ανέβασμα των αρχείων.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Παρακαλώ γράψε έναν μικρότερο τίτλο. Δεν μπορεί να είναι μεγαλύτερος από %1 χαρακτήρες.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Δεν μπορείς να ψηφίσεις την δημοσίευσή σου", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Οι εικόνες θεμάτων είναι απενεργοποιημένες", "invalid-file": "Άκυρο Αρχείο", "uploads-are-disabled": "Το ανέβασμα αρχείων έχει απενεργοποιηθεί", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Δεν μπορείς να συνομιλήσεις με τον εαυτό σου!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/el/groups.json b/public/language/el/groups.json index 305920e189..28c9fbe159 100644 --- a/public/language/el/groups.json +++ b/public/language/el/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/el/modules.json b/public/language/el/modules.json index 5646e812eb..4c82c03d4f 100644 --- a/public/language/el/modules.json +++ b/public/language/el/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Ημέρες", "chat.thirty_days": "30 Ημέρες", "chat.three_months": "3 Μήνες", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "Ο/Η %1 είπε στο %2:", "composer.user_said": "Ο/Η %1 είπε:", "composer.discard": "Είσαι σίγουρος/η πως θέλεις να πετάξεις αυτή την δημοσίευση;", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/el/pages.json b/public/language/el/pages.json index 1c2deb4489..c1fc315a96 100644 --- a/public/language/el/pages.json +++ b/public/language/el/pages.json @@ -5,7 +5,8 @@ "recent": "Πρόσφατα Θέματα", "users": "Εγγεγραμμένοι Χρήστες", "notifications": "Ειδοποιήσεις", - "tags": "Θέματα με ετικέτα \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Επεξεργασία του \"%1\"", "user.following": "Άτομα που ακολουθεί ο/η %1", "user.followers": "Άτομα που ακολουθούν τον/την %1", diff --git a/public/language/el/reset_password.json b/public/language/el/reset_password.json index 1ceecfd014..7af6489001 100644 --- a/public/language/el/reset_password.json +++ b/public/language/el/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Η Επαναφορά Κωδικού Εστάλη", "invalid_email": "Άκυρο Email / Το email δεν υπάρχει!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/el/search.json b/public/language/el/search.json index 11b0b90621..e650dbe2e5 100644 --- a/public/language/el/search.json +++ b/public/language/el/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 αποτελεσμα(τα) για \"%2\", (%3 δευτερόλεπτα)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/el/user.json b/public/language/el/user.json index 19a6d78974..984400744f 100644 --- a/public/language/el/user.json +++ b/public/language/el/user.json @@ -27,6 +27,7 @@ "chat": "Συνομιλία", "follow": "Ακολούθησε", "unfollow": "Μην Ακολουθείς", + "more": "More", "profile_update_success": "Το προφίλ ανανεώθηκε επιτυχώς!", "change_picture": "Αλλαγή Φωτογραφίας", "edit": "Επεξεργασία", @@ -47,7 +48,6 @@ "upload_picture": "Ανέβασμα φωτογραφίας", "upload_a_picture": "Ανέβασε μια φωτογραφία", "image_spec": "Μπορείς να ανεβάσεις αρχεία τύπου PNG, JPG ή GIF μόνο", - "max": "μέγιστο.", "settings": "Επιλογές", "show_email": "Εμφάνιση του email μου", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Κρυμμένο Emai", "hidden": "κρυμμένο", - "paginate_description": "Σελιδοποίηση των θεμάτων και των δημοσιεύσεων αντί για χρήση άπειρου scroll.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Θέματα ανά σελίδα", "posts_per_page": "Δημοσιεύσεις ανά σελίδα", - "notification_sounds": "Αναπαραγωγή ήχου όταν λαμβάνεις μια ειδοποίηση.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Επιλογές Περιήγησης", - "open_links_in_new_tab": "Άνοιγμα εξερχόμενων συνδέσμων σε νέα καρτέλα;", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Ακολούθα τα θέματα στα οποία απαντάς.", - "follow_topics_you_create": "Ακολούθα τα θέματα που δημιουργείς.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/en@pirate/category.json b/public/language/en@pirate/category.json index c2c0b64832..7e84700d2f 100644 --- a/public/language/en@pirate/category.json +++ b/public/language/en@pirate/category.json @@ -5,5 +5,8 @@ "browsing": "browsin'", "no_replies": "No one has replied to ye message", "share_this_category": "Share this category", - "ignore": "Ignore" + "watch": "Watch", + "ignore": "Ignore", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index 17465a20a3..867f331c3c 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "User banned", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Category disabled", "topic-locked": "Topic Locked", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/en@pirate/groups.json b/public/language/en@pirate/groups.json index d2314fdc29..5e301aa46a 100644 --- a/public/language/en@pirate/groups.json +++ b/public/language/en@pirate/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/en@pirate/modules.json b/public/language/en@pirate/modules.json index e43e497da3..f8e2e49f0b 100644 --- a/public/language/en@pirate/modules.json +++ b/public/language/en@pirate/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Days", "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/en@pirate/pages.json b/public/language/en@pirate/pages.json index 1bcf760876..afe6d74248 100644 --- a/public/language/en@pirate/pages.json +++ b/public/language/en@pirate/pages.json @@ -5,7 +5,8 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/en@pirate/reset_password.json b/public/language/en@pirate/reset_password.json index ba9f012ea6..8f727f0b92 100644 --- a/public/language/en@pirate/reset_password.json +++ b/public/language/en@pirate/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Password Reset Sent", "invalid_email": "Invalid Email / Email does not exist!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/en@pirate/search.json b/public/language/en@pirate/search.json index 9dad8b6eab..277c0a32bc 100644 --- a/public/language/en@pirate/search.json +++ b/public/language/en@pirate/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/en@pirate/user.json b/public/language/en@pirate/user.json index f0467e46e3..d93872539c 100644 --- a/public/language/en@pirate/user.json +++ b/public/language/en@pirate/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Follow", "unfollow": "Unfollow", + "more": "More", "profile_update_success": "Profile has been updated successfully!", "change_picture": "Change Picture", "edit": "Edit", @@ -47,7 +48,6 @@ "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", "image_spec": "You may only upload PNG, JPG, or GIF files", - "max": "max.", "settings": "Settings", "show_email": "Show My Email", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Email Hidden", "hidden": "hidden", - "paginate_description": "Paginate topics and posts instead of using infinite scroll.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Topics per Page", "posts_per_page": "Posts per Page", - "notification_sounds": "Play a sound when you receive a notification.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/en_GB/category.json b/public/language/en_GB/category.json index 3e131894bd..53a0eb3bc8 100644 --- a/public/language/en_GB/category.json +++ b/public/language/en_GB/category.json @@ -7,5 +7,9 @@ "no_replies": "No one has replied", "share_this_category": "Share this category", - "ignore": "Ignore" + "watch": "Watch", + "ignore": "Ignore", + + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index a8d4c042b5..40975c69b0 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -26,12 +26,13 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "User banned", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", @@ -45,19 +46,19 @@ "category-disabled": "Category disabled", "topic-locked": "Topic Locked", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", "invalid-title": "Invalid title!", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", "already-favourited": "You have already favourited this post", @@ -81,11 +82,14 @@ "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", + "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", "signature-too-long" : "Sorry, your signature cannot be longer than %1 characters.", + "about-me-too-long" : "Sorry, your about me cannot be longer than %1 characters.", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", diff --git a/public/language/en_GB/groups.json b/public/language/en_GB/groups.json index 09b1509171..644bd6a4cb 100644 --- a/public/language/en_GB/groups.json +++ b/public/language/en_GB/groups.json @@ -24,6 +24,8 @@ "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/en_GB/modules.json b/public/language/en_GB/modules.json index 12eeb0f660..68d305cfa4 100644 --- a/public/language/en_GB/modules.json +++ b/public/language/en_GB/modules.json @@ -16,8 +16,12 @@ "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/en_GB/pages.json b/public/language/en_GB/pages.json index 74e24a2e79..d9825d5800 100644 --- a/public/language/en_GB/pages.json +++ b/public/language/en_GB/pages.json @@ -5,7 +5,8 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/en_GB/reset_password.json b/public/language/en_GB/reset_password.json index 96ba318a8a..9f5b0dcc45 100644 --- a/public/language/en_GB/reset_password.json +++ b/public/language/en_GB/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Password Reset Sent", "invalid_email": "Invalid Email / Email does not exist!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } diff --git a/public/language/en_GB/search.json b/public/language/en_GB/search.json index 8c73511bda..304134918b 100644 --- a/public/language/en_GB/search.json +++ b/public/language/en_GB/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/en_GB/uploads.json b/public/language/en_GB/uploads.json index 5dc53ede1f..8cf9487901 100644 --- a/public/language/en_GB/uploads.json +++ b/public/language/en_GB/uploads.json @@ -1,5 +1,6 @@ { "uploading-file" : "Uploading the file...", "select-file-to-upload": "Select a file to upload!", - "upload-success": "File uploaded successfully!" + "upload-success": "File uploaded successfully!", + "maximum-file-size": "Maximum %1 kb" } \ No newline at end of file diff --git a/public/language/en_GB/user.json b/public/language/en_GB/user.json index f7460a8821..99f118ad5a 100644 --- a/public/language/en_GB/user.json +++ b/public/language/en_GB/user.json @@ -23,12 +23,14 @@ "watched": "Watched", "followers": "Followers", "following": "Following", + "aboutme": "About me", "signature": "Signature", "gravatar": "Gravatar", "birthday": "Birthday", "chat": "Chat", "follow": "Follow", "unfollow": "Unfollow", + "more": "More", "profile_update_success": "Profile has been updated successfully!", "change_picture": "Change Picture", @@ -51,7 +53,6 @@ "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", "image_spec": "You may only upload PNG, JPG, or GIF files", - "max": "max.", "settings": "Settings", "show_email": "Show My Email", @@ -76,20 +77,21 @@ "email_hidden": "Email Hidden", "hidden": "hidden", - "paginate_description" : "Paginate topics and posts instead of using infinite scroll.", + "paginate_description" : "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Topics per Page", "posts_per_page": "Posts per Page", - "notification_sounds" : "Play a sound when you receive a notification.", + "notification_sounds" : "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", - "grouptitle": "Select the group title you would like to display" + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } diff --git a/public/language/en_US/category.json b/public/language/en_US/category.json index 8245a2c365..be0b8e42f5 100644 --- a/public/language/en_US/category.json +++ b/public/language/en_US/category.json @@ -5,5 +5,8 @@ "browsing": "browsing", "no_replies": "No one has replied", "share_this_category": "Share this category", - "ignore": "Ignore" + "watch": "Watch", + "ignore": "Ignore", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index 17465a20a3..867f331c3c 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "User banned", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Category disabled", "topic-locked": "Topic Locked", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/en_US/groups.json b/public/language/en_US/groups.json index d2314fdc29..5e301aa46a 100644 --- a/public/language/en_US/groups.json +++ b/public/language/en_US/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/en_US/modules.json b/public/language/en_US/modules.json index 7cdd9b4c48..e86a8ce480 100644 --- a/public/language/en_US/modules.json +++ b/public/language/en_US/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Days", "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/en_US/pages.json b/public/language/en_US/pages.json index ca0cb61e2e..8f3c442101 100644 --- a/public/language/en_US/pages.json +++ b/public/language/en_US/pages.json @@ -5,7 +5,8 @@ "recent": "Recent Topics", "users": "Registered Users", "notifications": "Notifications", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Editing \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/en_US/reset_password.json b/public/language/en_US/reset_password.json index ba9f012ea6..8f727f0b92 100644 --- a/public/language/en_US/reset_password.json +++ b/public/language/en_US/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Password Reset Sent", "invalid_email": "Invalid Email / Email does not exist!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/en_US/search.json b/public/language/en_US/search.json index 9dad8b6eab..277c0a32bc 100644 --- a/public/language/en_US/search.json +++ b/public/language/en_US/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/en_US/user.json b/public/language/en_US/user.json index b6209de55a..ddfb4ac04a 100644 --- a/public/language/en_US/user.json +++ b/public/language/en_US/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Follow", "unfollow": "Unfollow", + "more": "More", "profile_update_success": "Profile has been updated successfully!", "change_picture": "Change Picture", "edit": "Edit", @@ -47,7 +48,6 @@ "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", "image_spec": "You may only upload PNG, JPG, or GIF files", - "max": "max.", "settings": "Settings", "show_email": "Show My Email", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Email Hidden", "hidden": "hidden", - "paginate_description": "Paginate topics and posts instead of using infinite scroll.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Topics per Page", "posts_per_page": "Posts per Page", - "notification_sounds": "Play a sound when you receive a notification.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/es/category.json b/public/language/es/category.json index 8c6b0fd5ca..dcb03b8ef3 100644 --- a/public/language/es/category.json +++ b/public/language/es/category.json @@ -5,5 +5,8 @@ "browsing": "viendo ahora", "no_replies": "Nadie ha respondido aún", "share_this_category": "Compartir esta categoría", - "ignore": "Ignorar" + "watch": "Ver", + "ignore": "Ignorar", + "watch.message": "Ahora estás viendo actualizaciones de esta categoría", + "ignore.message": "Ahora estás ignorando las actualizaciones de esta categoría" } \ No newline at end of file diff --git a/public/language/es/error.json b/public/language/es/error.json index e7e9723169..12132c317d 100644 --- a/public/language/es/error.json +++ b/public/language/es/error.json @@ -18,13 +18,14 @@ "username-taken": "Nombre de usuario ocupado", "email-taken": "Correo electrónico ocupado", "email-not-confirmed": "Su cuenta de correo electrónico no ha sido confirmada aún, por favor haga click aquí para confirmarla.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "No puedes usar el chat hasta que confirmes tu dirección de correo electrónico, por favor haz click aquí para confirmar tu correo.", "no-email-to-confirm": "Este foro requiere confirmación de su email, por favor pulse aquí para introducir un email", "email-confirm-failed": "No se ha podido confirmar su email, por favor inténtelo de nuevo más tarde.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Nombre de usuario es demasiado corto", "username-too-long": "Nombre de usuario demasiado largo", "user-banned": "Usuario baneado", - "user-too-new": "Lo sentimos, es necesario que esperes %1 segudos antes poder hacer tu primera publicación", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "La categoría no existe", "no-topic": "El tema no existe", "no-post": "La publicación no existe", @@ -35,17 +36,17 @@ "no-emailers-configured": "No se ha cargado ningún plugin de email, así que no se pudo enviar el email de prueba.", "category-disabled": "Categoría deshabilitada", "topic-locked": "Tema bloqueado", - "post-edit-duration-expired": "Sólo puedes editar mensajes durante %1 segundos después de haberlo escrito", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Por favor, espera a que terminen las subidas.", - "content-too-short": "Por favor introduzca una publicación más larga. Las publicaciones deben contener al menos %1 caracteres.", - "content-too-long": "Por favor introduzca un mensaje más corto. Los mensajes no pueden exceder los %1 caracteres.", - "title-too-short": "Por favor introduzca un título más largo. Los títulos deben contener al menos %1 caracteres.", - "title-too-long": "Por favor, introduce un título más corto, que no sobrepase los %1 caracteres.", - "too-many-posts": "Solo puedes publicar una vez cada %1 segundos - por favor espere antes de volver a publicar", - "too-many-posts-newbie": "Como nuevo usuario, solo puedes publicar una vez cada %1 segundos hasta hayas ganado una reputación de %2 - por favor espere antes de volver a publicar", - "tag-too-short": "Por favor introduce una etiqueta más larga. Las etiquetas deben contener por lo menos %1 caracteres", - "tag-too-long": "Por favor introduce una etiqueta más pequeña. Las etiquetas no pueden exceder los %1 caracteres", - "file-too-big": "El tamaño de fichero máximo es de %1 kbs - por favor, suba un fichero más pequeño", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "No puedes votar tus propios posts", "already-favourited": "Ya ha marcado esta publicación como favorita", "already-unfavourited": "Ya ha desmarcado esta publicación como favorita", @@ -62,10 +63,11 @@ "post-already-restored": "Esta publicación ya ha sido restaurada", "topic-already-deleted": "Este tema ya ha sido borrado", "topic-already-restored": "Este tema ya ha sido restaurado", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Las miniaturas de los temas están deshabilitadas.", "invalid-file": "Archivo no válido", "uploads-are-disabled": "Las subidas están deshabilitadas.", - "signature-too-long": "Lo sentimos, pero tu firma no puede ser más larga de %1 caracteres.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "¡No puedes conversar contigo mismo!", "chat-restricted": "Este usuario tiene restringidos los mensajes de chat. Los usuarios deben seguirte antes de que pueda charlar con ellos", "too-many-messages": "Has enviado demasiados mensajes, por favor espera un poco.", diff --git a/public/language/es/groups.json b/public/language/es/groups.json index 22a80ba19a..70aaf339a1 100644 --- a/public/language/es/groups.json +++ b/public/language/es/groups.json @@ -20,6 +20,8 @@ "details.kick": "Expulsar", "details.owner_options": "Administración De Grupo", "details.group_name": "Nombre de Grupo", + "details.member_count": "Contar Miembro", + "details.creation_date": "Fecha de Creacion", "details.description": "Descripción", "details.badge_preview": "Previsualización de Insignia", "details.change_icon": "Cambiar Icono", diff --git a/public/language/es/modules.json b/public/language/es/modules.json index a63418d2c6..857135bde1 100644 --- a/public/language/es/modules.json +++ b/public/language/es/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 días", "chat.thirty_days": "30 días", "chat.three_months": "3 meses", + "composer.compose": "Crear", + "composer.show_preview": "Ver Previsualización", + "composer.hide_preview": "Ocultar Previsualización", "composer.user_said_in": "%1 dijo en %2:", "composer.user_said": "%1 dijo:", "composer.discard": "¿Estás seguro de que deseas descartar este mensaje?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Enviar y Bloquear", + "composer.toggle_dropdown": "Alternar desplegable" } \ No newline at end of file diff --git a/public/language/es/notifications.json b/public/language/es/notifications.json index 3a9e36bccb..03a9b10b9b 100644 --- a/public/language/es/notifications.json +++ b/public/language/es/notifications.json @@ -1,6 +1,6 @@ { "title": "Notificaciones", - "no_notifs": "No tiene nuevas notificaciones", + "no_notifs": "No tienes nuevas notificaciones", "see_all": "Ver todas las notificaciones", "mark_all_read": "Marcar todas las notificaciones cómo leídas", "back_to_home": "Volver a %1", diff --git a/public/language/es/pages.json b/public/language/es/pages.json index f8beed5e43..4a2374bc06 100644 --- a/public/language/es/pages.json +++ b/public/language/es/pages.json @@ -5,7 +5,8 @@ "recent": "Temas recientes", "users": "Usuarios registrados", "notifications": "Notificaciones", - "tags": "Temas etiquetados bajo \"%1\"", + "tags": "Etiquetas", + "tag": "Temas etiquetados en \"%1\"", "user.edit": "Editando \"%1\"", "user.following": "Gente que sigue %1 ", "user.followers": "Seguidores de %1", @@ -14,7 +15,7 @@ "user.groups": "%1's Grupos", "user.favourites": "Publicaciones favoritas de %1 ", "user.settings": "Preferencias de usuario", - "user.watched": "Topics watched by %1", + "user.watched": "Temas vistos por 1%", "maintenance.text": "%1 está en mantenimiento actualmente. Por favor vuelva en otro momento.", "maintenance.messageIntro": "Adicionalmente, la administración ha dejado este mensaje:" } \ No newline at end of file diff --git a/public/language/es/reset_password.json b/public/language/es/reset_password.json index da269f5448..88feed2371 100644 --- a/public/language/es/reset_password.json +++ b/public/language/es/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Restablecimiento de contraseña enviado", "invalid_email": "¡Correo electrónico no válido o inexistente!", "password_too_short": "La contraseña introducida es demasiado corta, por favor introduzca una contraseña diferente.", - "passwords_do_not_match": "Las dos contraseñas introducidas no concuerdan." + "passwords_do_not_match": "Las dos contraseñas introducidas no concuerdan.", + "password_expired": "Tu contraseña ha caducado, por favor elige una contraseña nueva" } \ No newline at end of file diff --git a/public/language/es/search.json b/public/language/es/search.json index 135144c2b3..06ace7e9a9 100644 --- a/public/language/es/search.json +++ b/public/language/es/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 resuldado(s) coinciden con \"%2\". (%3 segundos)", "no-matches": "No se encontraron coincidencias", + "advanced-search": "Búsqueda Avanzada", "in": "En", - "by": "Por", "titles": "Títulos", "titles-posts": "Títulos y publicaciones", "posted-by": "Publicado por", diff --git a/public/language/es/tags.json b/public/language/es/tags.json index 260ff42eef..ebb1bc6072 100644 --- a/public/language/es/tags.json +++ b/public/language/es/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "No hay temas con esta etiqueta.", "tags": "Etiquetas", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Introduce aquí las etiquetas, entre 1% y 2% caracteres cada una.", "enter_tags_here_short": "Introduzca las etiquetas...", "no_tags": "Aún no hay etiquetas." } \ No newline at end of file diff --git a/public/language/es/user.json b/public/language/es/user.json index 7accd46f09..09782de1b5 100644 --- a/public/language/es/user.json +++ b/public/language/es/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Seguir", "unfollow": "Dejar de seguir", + "more": "Más", "profile_update_success": "¡El perfil ha sido actualizado correctamente!", "change_picture": "Cambiar imágen", "edit": "Editar", @@ -47,7 +48,6 @@ "upload_picture": "Subir foto", "upload_a_picture": "Subir una foto", "image_spec": "Sólo puedes subir imágenes en formato PNG, JPG o GIF.", - "max": "máx.", "settings": "Opciones", "show_email": "Mostrar mi correo electrónico", "show_fullname": "Mostrar mi nombre completo", @@ -60,7 +60,7 @@ "digest_monthly": "Mensualmente", "send_chat_notifications": "Enviar un email si recibo un mensaje de chat cuando no esté en línea.", "send_post_notifications": "Enviarme un email cuando se realicen contestaciones en los temas en los que estoy subscrito", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Algunos cambios de configuración requieren una recarga la página. Haz click aquí para recargar la página.", "has_no_follower": "Este usuario no tiene seguidores :(", "follows_no_one": "Este miembro no sigue a nadie :(", "has_no_posts": "Este usuario aún no ha publicado nada.", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Este usuario todavía no ha visto ninguna publicación.", "email_hidden": "Correo electrónico oculto", "hidden": "oculto", - "paginate_description": "Paginar hilos y mensajes en lugar de usar desplazamiento infinito.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Temas por página", "posts_per_page": "Post por página", - "notification_sounds": "Reproducir un sonido al recibir una notificación.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Preferencias de navegación.", - "open_links_in_new_tab": "¿Abrir los enlaces externos en una nueva pestaña?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Activar la búsqueda \"in-topic\"", - "topic_search_help": "Si está activada, la búsqueda 'in-topic' invalidará el comportamiento por defecto del navegador de buscar sólo en la página mostrada y le permitirá entonces buscar en el tema al completo, en vez de hacer una búsqueda únicamente sobre el contenido de la pantalla.", - "follow_topics_you_reply_to": "Seguir los temas en las que respondes.", - "follow_topics_you_create": "Seguir publicaciones que creas.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Selecciona el título del grupo que deseas visualizar", + "no-group-title": "Sin título de grupo" } \ No newline at end of file diff --git a/public/language/et/category.json b/public/language/et/category.json index d3c0f6e6bd..bbaa42bee5 100644 --- a/public/language/et/category.json +++ b/public/language/et/category.json @@ -1,9 +1,12 @@ { "new_topic_button": "Uus teema", - "guest-login-post": "Log in to post", + "guest-login-post": "Postitamiseks logi sisse", "no_topics": "Kahjuks ei leidu siin kategoorias ühtegi teemat.
Soovid postitada?", "browsing": "vaatab", "no_replies": "Keegi pole vastanud", "share_this_category": "Jaga seda kategooriat", - "ignore": "Ignoreeri" + "watch": "Vaata", + "ignore": "Ignoreeri", + "watch.message": "Jälgid nüüdsest teateid sellest kategooriast", + "ignore.message": "Ignoreerid nüüdsest teateid sellest kategooriast" } \ No newline at end of file diff --git a/public/language/et/email.json b/public/language/et/email.json index f290435e75..9f3a49318e 100644 --- a/public/language/et/email.json +++ b/public/language/et/email.json @@ -1,8 +1,8 @@ { - "password-reset-requested": "Password Reset Requested - %1!", - "welcome-to": "Welcome to %1", - "greeting_no_name": "Hello", - "greeting_with_name": "Hello %1", + "password-reset-requested": "Parooli muutmise taotlus - %1!", + "welcome-to": "Tere tulemast %1", + "greeting_no_name": "Tere", + "greeting_with_name": "Tere %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", "welcome.cta": "Click here to confirm your email address", diff --git a/public/language/et/error.json b/public/language/et/error.json index 0bbf35bd3a..776586b76c 100644 --- a/public/language/et/error.json +++ b/public/language/et/error.json @@ -12,70 +12,72 @@ "invalid-title": "Vigane pealkiri!", "invalid-user-data": "Vigased kasutaja andmed", "invalid-password": "Vigane parool", - "invalid-username-or-password": "Please specify both a username and password", - "invalid-search-term": "Invalid search term", + "invalid-username-or-password": "Palun täpsusta kasutajanime ja parooli", + "invalid-search-term": "Vigane otsingusõna", "invalid-pagination-value": "Vigane lehe väärtus", "username-taken": "Kasutajanimi on juba võetud", "email-taken": "Email on võetud", - "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "email-not-confirmed": "Su emaili aadress ei ole kinnitatud, vajuta siia et kinnitada.", + "email-not-confirmed-chat": "Sõnumeid ei ole võimalik enne saata kui sinu email on kinnitatud. Kinnitamiseks vajuta siia.", + "no-email-to-confirm": "See foorum nõuab emaili kinnitust, palun vajuta siia, et sisestada email", + "email-confirm-failed": "Meil ei õnnestunud sinu emaili kinnitada, proovi hiljem uuesti.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Kasutajanimi on liiga lühike", - "username-too-long": "Username too long", + "username-too-long": "Kasutajanimi on liiga pikk", "user-banned": "Kasutaja bannitud", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", - "no-category": "Category does not exist", - "no-topic": "Topic does not exist", - "no-post": "Post does not exist", - "no-group": "Group does not exist", - "no-user": "User does not exist", - "no-teaser": "Teaser does not exist", - "no-privileges": "You do not have enough privileges for this action.", - "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", + "no-category": "Kategooriat ei eksisteeri", + "no-topic": "Teemat ei eksisteeri", + "no-post": "Postitust ei eksisteeri", + "no-group": "Gruppi ei eksisteeri", + "no-user": "Kasutajat ei eksisteeri", + "no-teaser": "Eelvaadet ei eksisteeri", + "no-privileges": "Sul pole piisavalt õigusi.", + "no-emailers-configured": "Emaili rakendust ei ole laetud, seega test emaili ei ole võimalik saata", "category-disabled": "Kategooria keelatud", "topic-locked": "Teema lukustatud", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Palun oota, kuni üleslaadimised on laetud.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Palun sisesta lühem pealkiri. Pealkirjad ei saa olla pikemad kui %1 tähemärki.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Sa ei saa hääletada enda postituse poolt", - "already-favourited": "You have already favourited this post", - "already-unfavourited": "You have already unfavourited this post", + "already-favourited": "Sa juba märkisid selle postituse lemmikuks", + "already-unfavourited": "Sa juba eemaldasid selle postituse lemmikute hulgast", "cant-ban-other-admins": "Sa ei saa bannida teisi administraatoreid!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "invalid-image-type": "Vigane pildi formaat. Lubatud formaadid on: %1", + "invalid-image-extension": "Vigane pildi formaat", + "invalid-file-type": "Vigane faili formaat. Lubatud formaadid on: %1", "group-name-too-short": "Grupi nimi liiga lühike", "group-already-exists": "Grupp juba eksisteerib", "group-name-change-not-allowed": "Grupi nimevahetus ei ole lubatud", - "group-already-member": "You are already part of this group", - "group-needs-owner": "This group requires at least one owner", - "post-already-deleted": "This post has already been deleted", - "post-already-restored": "This post has already been restored", - "topic-already-deleted": "This topic has already been deleted", - "topic-already-restored": "This topic has already been restored", + "group-already-member": "Sa oled juba selle grupi liige", + "group-needs-owner": "See grupp nõuab vähemalt ühte omanikku", + "post-already-deleted": "Postitus on juba kustutatud", + "post-already-restored": "Postitus on juba taastatud", + "topic-already-deleted": "Teema on juba kustutatud", + "topic-already-restored": "Teema on juba taastatud", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Teema thumbnailid on keelatud.", "invalid-file": "Vigane fail", "uploads-are-disabled": "Üleslaadimised on keelatud", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Sa ei saa endaga vestelda!", - "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", - "too-many-messages": "You have sent too many messages, please wait awhile.", - "reputation-system-disabled": "Reputation system is disabled.", - "downvoting-disabled": "Downvoting is disabled", + "chat-restricted": "Kasutaja on piiranud sõnumite saatmist. Privaatsõnumi saatmiseks peab kasutaja sind jälgima", + "too-many-messages": "Oled saatnud liiga palju sõnumeid, oota natukene.", + "reputation-system-disabled": "Reputatsiooni süsteem ei ole aktiveeritud", + "downvoting-disabled": "Negatiivsete häälte andmine ei ole võimaldatud", "not-enough-reputation-to-downvote": "Sul ei ole piisavalt reputatsiooni, et anda negatiivset hinnangut sellele postitusele.", - "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", - "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", - "registration-error": "Registration Error", - "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "not-enough-reputation-to-flag": "Sul ei ole piisavalt reputatsiooni, et seda postitust raporteerida", + "reload-failed": "\"%1\" värskendamisel tekkis süsteemne viga. Foorum ei lakka töötamast, kuid peaksid kindlasti eemaldama enne värskendamist tehtud muudatused.", + "registration-error": "Viga registreerimisel", + "parse-error": "Midagi läks valesti...", + "wrong-login-type-email": "Sisse logimiseks kasuta oma emaili", + "wrong-login-type-username": "Sisse logimiseks kasuta oma kasutajanime" } \ No newline at end of file diff --git a/public/language/et/global.json b/public/language/et/global.json index 5e14fb2623..b383959064 100644 --- a/public/language/et/global.json +++ b/public/language/et/global.json @@ -3,10 +3,10 @@ "search": "Otsi", "buttons.close": "Sulge", "403.title": "Ligipääs puudub", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", - "403.login": "Perhaps you should try logging in?", + "403.message": "Tundub, et sul pole piisvalt õigusi selle lehe vaatamiseks. ", + "403.login": "Äkki peaksid sisse logima?", "404.title": "Ei leitud", - "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "404.message": "Tundub, et lehte mida otsid, ei eksisteeri. Mine tagasi avalehele.", "500.title": "Süsteemi viga", "500.message": "Oih! Midagi läks valesti!", "register": "Registreeri", @@ -27,7 +27,7 @@ "header.tags": "Märksõnad", "header.popular": "Populaarne", "header.users": "Kasutajad", - "header.groups": "Groups", + "header.groups": "Grupid", "header.chats": "Vestlused", "header.notifications": "Teated", "header.search": "Otsi", @@ -46,15 +46,15 @@ "online": "Sees", "users": "Kasutajad", "topics": "Teemad", - "posts": "Postitused", - "views": "Vaatamised", + "posts": "Postitust", + "views": "Vaatamist", "reputation": "Reputatsioon", "read_more": "loe veel", "posted_ago_by_guest": "postitas %1 külaline", "posted_ago_by": "postitas %1 kasutaja %2", "posted_ago": "postitatud %1", "posted_in_ago_by_guest": "külaline postitas kategooriasse %1 %2 ", - "posted_in_ago_by": "%3 postitatas %2 kategooriasse %1", + "posted_in_ago_by": "%3 postitas %2 kategooriasse %1", "posted_in_ago": "postitas kategooriasse %1 %2", "replied_ago": "vastas %1", "user_posted_ago": "%1 postitas %2", @@ -75,7 +75,7 @@ "updated.title": "Foorum on uuendatud", "updated.message": "See foorum uuendati just kõige uuemale versioonile. Vajuta siia et värskendada veebilehte.", "privacy": "Privaatsus", - "follow": "Follow", - "unfollow": "Unfollow", + "follow": "Jälgi", + "unfollow": "Ära jälgi", "delete_all": "Kustuta kõik" } \ No newline at end of file diff --git a/public/language/et/groups.json b/public/language/et/groups.json index d2314fdc29..56642324bd 100644 --- a/public/language/et/groups.json +++ b/public/language/et/groups.json @@ -1,34 +1,36 @@ { - "groups": "Groups", - "view_group": "View Group", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", - "details.title": "Group Details", - "details.members": "Member List", - "details.pending": "Pending Members", - "details.has_no_posts": "This group's members have not made any posts.", - "details.latest_posts": "Latest Posts", - "details.private": "Private", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "details.group_name": "Group Name", - "details.description": "Description", - "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", - "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", - "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", - "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "groups": "Grupid", + "view_group": "Vaata gruppi", + "owner": "Grupi omanik", + "new_group": "Loo uus grupp", + "no_groups_found": "Ei ole ühtegi gruppi", + "pending.accept": "Aktsepteeri", + "pending.reject": "Lükka tagasi", + "cover-instructions": "Lohista kaanepilt siia ning vajuta salvesta", + "cover-change": "Muuda", + "cover-save": "Salvesta", + "cover-saving": "Salvestamine", + "details.title": "Grupi detailid", + "details.members": "Liikmete nimekiri", + "details.pending": "Otsust ootavad liikmed", + "details.has_no_posts": "Selle grupi liikmed ei ole teinud ühtegi postitust.", + "details.latest_posts": "Viimased postitused", + "details.private": "Privaatne", + "details.grant": "Anna/võta omanikuõigused", + "details.kick": "Viska välja", + "details.owner_options": "Grupi haldamine", + "details.group_name": "Grupi nimi", + "details.member_count": "Liikmete arv", + "details.creation_date": "Algatamise kuupäev", + "details.description": "Kirjeldus", + "details.badge_preview": "Embleemi eelvaade", + "details.change_icon": "Vaheta ikooni", + "details.change_colour": "Vaheta värvi", + "details.badge_text": "Embleemi kiri", + "details.userTitleEnabled": "Näita embleemi", + "details.private_help": "Kui sisse lülitatud, siis grupiga liitumine nõuab grupi omaniku nõusolekut", + "details.hidden": "Peidetud", + "details.hidden_help": "Kui sisse lülitatud, siis seda gruppi ei kuvata gruppide nimekirjas ning liikmed tuleb lisada manuaalselt", + "event.updated": "Grupi lisainformatsiooni on uuendatud", + "event.deleted": "Grupp \"%1\" on kustutatud" } \ No newline at end of file diff --git a/public/language/et/login.json b/public/language/et/login.json index 20bd7e3f0d..6c283a8b83 100644 --- a/public/language/et/login.json +++ b/public/language/et/login.json @@ -1,7 +1,7 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username-email": "Kasutajanimi / E-mail", + "username": "Kasutajanimi", + "email": "E-mail.", "remember_me": "Mäleta mind?", "forgot_password": "Unustasid parooli?", "alternative_logins": "Alternatiivsed sisse logimise võimalused", diff --git a/public/language/et/modules.json b/public/language/et/modules.json index 2ddda85058..6fa3be280f 100644 --- a/public/language/et/modules.json +++ b/public/language/et/modules.json @@ -12,11 +12,15 @@ "chat.message-history": "Sõnumite ajalugu", "chat.pop-out": "Pop-out vestlus", "chat.maximize": "Suurenda", - "chat.seven_days": "7 Days", - "chat.thirty_days": "30 Days", - "chat.three_months": "3 Months", + "chat.seven_days": "7 Päeva", + "chat.thirty_days": "30 Päeva", + "chat.three_months": "3 Kuud", + "composer.compose": "Koosta", + "composer.show_preview": "Kuva eelvaadet", + "composer.hide_preview": "Peida eelvaade", "composer.user_said_in": "%1 ütles %2:", "composer.user_said": "%1 ütles:", "composer.discard": "Oled kindel, et soovid selle postituse tühistada?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Kinnita ja Lukusta", + "composer.toggle_dropdown": "Aktiveeri rippmenüü" } \ No newline at end of file diff --git a/public/language/et/notifications.json b/public/language/et/notifications.json index 46718ce1d4..24c90fce91 100644 --- a/public/language/et/notifications.json +++ b/public/language/et/notifications.json @@ -2,24 +2,24 @@ "title": "Teated", "no_notifs": "Sul pole uusi teateid", "see_all": "Vaata kõiki teateid", - "mark_all_read": "Mark all notifications read", + "mark_all_read": "Märgi kõik teavitused loetuks", "back_to_home": "Tagasi %1", "outgoing_link": "Väljaminev link", - "outgoing_link_message": "You are now leaving %1.", - "continue_to": "Continue to %1", - "return_to": "Return to %1", + "outgoing_link_message": "Lahkud foorumist %1.", + "continue_to": "Jätka %1", + "return_to": "Pöördu tagasi %1", "new_notification": "Uus teade", "you_have_unread_notifications": "Sul ei ole lugemata teateid.", "new_message_from": "Uus sõnum kasutajalt %1", - "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", - "favourited_your_post_in": "%1 has favourited your post in %2.", - "user_flagged_post_in": "%1 flagged a post in %2", + "upvoted_your_post_in": "%1 hääletas sinu postituse poolt teemas %2.", + "moved_your_post": "%1 liigutas sinu postitust.", + "moved_your_topic": "%1 liigutas sinu teemat.", + "favourited_your_post_in": "%1 märgistas sinu postituse lemmikuks teemas %2.", + "user_flagged_post_in": "%1 raporteeris postitust %2", "user_posted_to": "Kasutaja %1 postitas vastuse teemasse %2", - "user_posted_topic": "%1 has posted a new topic: %2", + "user_posted_topic": "%1 on postitanud uue teema: %2", "user_mentioned_you_in": "%1 mainis sind postituses %2", - "user_started_following_you": "%1 started following you.", + "user_started_following_you": "%1 hakkas sind jälgima.", "email-confirmed": "Emaili aadress kinnitatud", "email-confirmed-message": "Täname, et kinnitasite oma emaili aadressi. Teie kasutaja omn nüüd täielikult aktiveeritud.", "email-confirm-error-message": "Emaili aadressi kinnitamisel tekkis viga. Võibolla kinnituskood oli vale või aegunud.", diff --git a/public/language/et/pages.json b/public/language/et/pages.json index d5cffc9d46..136b1463db 100644 --- a/public/language/et/pages.json +++ b/public/language/et/pages.json @@ -5,16 +5,17 @@ "recent": "Hiljutised teemad", "users": "Registreeritud kasutajad", "notifications": "Teated", - "tags": "Teemad märksõnadega \"%1\"", + "tags": "Märksõnad", + "tag": "Teemad märksõnadega \"%1\"", "user.edit": "Muudan \"%1\"", "user.following": "Kasutaja %1 jälgib", "user.followers": "Kasutajad, kes jälgivad %1", "user.posts": "Postitused, mis on tehtud kasutaja %1 poolt", "user.topics": "Teemad on kirjutanud %1", - "user.groups": "%1's Groups", + "user.groups": "%1's grupid", "user.favourites": "%1's lemmikud postitused", "user.settings": "Kasutaja sätted", - "user.watched": "Topics watched by %1", + "user.watched": "Teemasid jälgib %1 kasutajat", "maintenance.text": "%1 foorumil on käimas hooldustööd. Palun külastage meid mõne aja pärast uuesti.", "maintenance.messageIntro": "Administraator on jätnud ka omaltpoolt sõnumi:" } \ No newline at end of file diff --git a/public/language/et/recent.json b/public/language/et/recent.json index 272a2b9131..96882af6a3 100644 --- a/public/language/et/recent.json +++ b/public/language/et/recent.json @@ -6,14 +6,14 @@ "year": "Aasta", "alltime": "Kogu aja vältel", "no_recent_topics": "Hetkel ei ole hiljutisi teemasid.", - "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "no_popular_topics": "Ühtegi populaarset teemat ei leidu.", + "there-is-a-new-topic": "On loodud uus teema.", + "there-is-a-new-topic-and-a-new-post": "On loodud uus teema ning postitus.", + "there-is-a-new-topic-and-new-posts": "On loodud uus teema ning %1 uut postitust.", + "there-are-new-topics": "On loodud %1 uut teemat.", + "there-are-new-topics-and-a-new-post": "On loodud %1 uut teemat ning uus postitus.", + "there-are-new-topics-and-new-posts": "On loodud %1 uut teemat ning %2 uut postitust.", + "there-is-a-new-post": "On loodud uus postitus.", + "there-are-new-posts": "On loodud %1 uut postitust.", + "click-here-to-reload": "Värskendamiseks vajuta siia." } \ No newline at end of file diff --git a/public/language/et/reset_password.json b/public/language/et/reset_password.json index 98e4fe4fae..dec7d5ab7d 100644 --- a/public/language/et/reset_password.json +++ b/public/language/et/reset_password.json @@ -11,6 +11,7 @@ "enter_email_address": "Sisesta emaili aadress", "password_reset_sent": "Saadetud", "invalid_email": "Vigane emaili aadress / emaili aadressi ei ekisteeri!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "Sisestatud parool on liiga lühike, palun vali teine parool.", + "passwords_do_not_match": "Sisestatud paroolid ei ühti.", + "password_expired": "Sinu parool on aegunud, palun vali uus parool" } \ No newline at end of file diff --git a/public/language/et/search.json b/public/language/et/search.json index 661cebbacd..a9f4e17dce 100644 --- a/public/language/et/search.json +++ b/public/language/et/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 tulemus(t) mis vastavad otsingule \"%2\", (%3 sekundit)", - "no-matches": "No matches found", + "no-matches": "Vasteid ei leitud", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/et/tags.json b/public/language/et/tags.json index 87b8332fad..ac6ee268c0 100644 --- a/public/language/et/tags.json +++ b/public/language/et/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Teemasid, mis sisaldaksid seda märksõna, ei eksisteeri.", "tags": "Märksõnad", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Sisesta märksõnad siia, %1 kuni %2 tähemärki märksõna kohta.", "enter_tags_here_short": "Sisesta märksõnu...", "no_tags": "Siin ei ole veel ühtegi märksõna." } \ No newline at end of file diff --git a/public/language/et/topic.json b/public/language/et/topic.json index 8992993fb9..f5d72479c9 100644 --- a/public/language/et/topic.json +++ b/public/language/et/topic.json @@ -12,7 +12,7 @@ "notify_me": "Saa teateid uutest postitustest selles teemas", "quote": "Tsiteeri", "reply": "Vasta", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Logi sisse, et vastata", "edit": "Muuda", "delete": "Kustuta", "purge": "Kustuta", @@ -27,7 +27,7 @@ "bookmark_instructions": "Vajuta siia, et pöörduda tagasi oma viimasesse asukohta või sulge.", "flag_title": "Märgista see postitus modereerimiseks", "flag_confirm": "Oled kindel, et soovid märgistada antud postituse?", - "flag_success": "See posits on nüüd märgistatud modereerimiseks.", + "flag_success": "See postitus on nüüd märgistatud modereerimiseks.", "deleted_message": "See teema on kustutatud. Ainult kasutajad kellel on piisavalt õigusi saavad seda näha.", "following_topic.message": "Sulle ei edastata enam teateid uutest postitustest kui keegi postitab siia teemasse.", "not_following_topic.message": "Sulle ei edastata enam teateid uutest postitustest siin teemas.", @@ -75,7 +75,7 @@ "fork_no_pids": "Sa ei ole postitusi valinud!", "fork_success": "Edukalt ''forkisid'' teema! Vajuta siia, et vaadata loodud teemat.", "composer.title_placeholder": "Sisesta teema pealkiri siia...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Nimi", "composer.discard": "Katkesta", "composer.submit": "Postita", "composer.replying_to": "Vastad %1'le", @@ -95,5 +95,5 @@ "oldest_to_newest": "Vanematest uuemateni", "newest_to_oldest": "Uuematest vanemateni", "most_votes": "Kõige rohkem hääli", - "most_posts": "Most posts" + "most_posts": "Kõige rohkem postitusi" } \ No newline at end of file diff --git a/public/language/et/user.json b/public/language/et/user.json index 2e2b7a37fa..6c015502db 100644 --- a/public/language/et/user.json +++ b/public/language/et/user.json @@ -2,8 +2,8 @@ "banned": "Banned", "offline": "Väljas", "username": "Kasutajanimi", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Liitumiskuupäev", + "postcount": "Postitusi", "email": "Email", "confirm_email": "Kinnita email", "delete_account": "Kustuta kasutaja", @@ -18,7 +18,7 @@ "profile_views": "Vaatamisi", "reputation": "Reputatsioon", "favourites": "Lemmikud", - "watched": "Watched", + "watched": "Vaadatud", "followers": "Jälgijad", "following": "Jälgimised", "signature": "Allkiri", @@ -27,6 +27,7 @@ "chat": "Vestlus", "follow": "Jälgi", "unfollow": "Ära jälgi enam", + "more": "Rohkem", "profile_update_success": "Profiil edukalt uuendatud!", "change_picture": "Vaheta pilti", "edit": "Muuda", @@ -47,7 +48,6 @@ "upload_picture": "Laadi pilt", "upload_a_picture": "Lae pilt üles", "image_spec": "Failid peavad olema PNG, JPG või GIF vormingus.", - "max": "max.", "settings": "Seaded", "show_email": "Näita minu emaili", "show_fullname": "Näita minu täisnime", @@ -59,24 +59,25 @@ "digest_weekly": "Iga nädal", "digest_monthly": "Iga kuu", "send_chat_notifications": "Saada mulle email kui mulle saabub uus sõnum ja ma ei ole antud hetkel online", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "send_post_notifications": "Saada e-mail kui minu poolt jälgitavatele teemadele vastatakse", + "settings-require-reload": "Muudatused seadetes nõuavad lehe uuesti laadimist. Lehe värskendamiseks vajuta siia.", "has_no_follower": "Sellel kasutajal pole ühtegi jälgijat :(", "follows_no_one": "See kasutaja ei jälgi kedagi :(", "has_no_posts": "See kasutaja pole midagi postitanud veel.", "has_no_topics": "See kasutaja pole vele ühtegi teemat postitanud.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "See kasutaja pole vaadanud ühtegi teemat veel.", "email_hidden": "Peidetud email", "hidden": "peidetud", - "paginate_description": "Nummerda leheküljed ja postitused ning ära kasuta ''lõputut scrolli''.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Teemasi ühe lehekülje kohta", "posts_per_page": "Postitusi ühe lehekülje kohta", - "notification_sounds": "Tee häält, kui saabub teade.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Sirvimis sätted", - "open_links_in_new_tab": "Ava väljaminevad lingid uues vaheaknas?", - "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Järgi teemasid millele vastuse kirjutad.", - "follow_topics_you_create": "Järgi teemasid, mis on sinu loodud.", - "grouptitle": "Select the group title you would like to display" + "open_links_in_new_tab": "Open outgoing links in new tab", + "enable_topic_searching": "Võimalda teemasisene otsing", + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Vali grupile tiitel mida kuvada soovid", + "no-group-title": "Grupi tiitel puudub" } \ No newline at end of file diff --git a/public/language/et/users.json b/public/language/et/users.json index ac83e6b160..40d9110d57 100644 --- a/public/language/et/users.json +++ b/public/language/et/users.json @@ -5,8 +5,8 @@ "search": "Otsi", "enter_username": "Sisesta kasutajanimi, keda soovid otsida", "load_more": "Lae veel", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "users-found-search-took": "%1 kasutaja(t) leiti! Otsing kestis %2 sekundit.", + "filter-by": "Filtreeri", + "online-only": "Ainult seesolevad", + "picture-only": "Ainult pilt" } \ No newline at end of file diff --git a/public/language/fa_IR/category.json b/public/language/fa_IR/category.json index b5b2749c70..d67bba2cd7 100644 --- a/public/language/fa_IR/category.json +++ b/public/language/fa_IR/category.json @@ -1,9 +1,12 @@ { "new_topic_button": "جستار تازه", - "guest-login-post": "Log in to post", - "no_topics": "هیچ جستاری در این دسته نیست.
چرا شما یکی نفرستید؟", + "guest-login-post": "برای ثبت نظر وارد شوید", + "no_topics": "هیچ پستی در این دسته نیست.
چرا شما یکی نفرستید؟", "browsing": "بیننده‌ها", "no_replies": "هیچ کسی پاسخ نداده است.", "share_this_category": "به اشتراک‌گذاری این دسته", - "ignore": "Ignore" + "watch": "نظارت کردن", + "ignore": "نادیده گرفتن", + "watch.message": "در حال حاظر شما به روز رسانی های این دسته را دنبال می کنید", + "ignore.message": "در حال حاظر شما به روز رسانی های این دسته را نادیده میگیرد" } \ No newline at end of file diff --git a/public/language/fa_IR/email.json b/public/language/fa_IR/email.json index 206fe1823b..1ed97a0d8c 100644 --- a/public/language/fa_IR/email.json +++ b/public/language/fa_IR/email.json @@ -1,28 +1,28 @@ { - "password-reset-requested": "Password Reset Requested - %1!", + "password-reset-requested": "درخواست گذرواژه مجدد- %1!", "welcome-to": "به 1% خوش آمدید", "greeting_no_name": "سلام", "greeting_with_name": "سلام 1%", - "welcome.text1": "Thank you for registering with %1!", - "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", + "welcome.text1": "متشکر بابت ثبت نام در %1!", + "welcome.text2": "برای فعال کردن کامل اکانت شما، ما نیاز داریم تا اطمینان حاصل کنیم که شما مالک ایمیلی که با ان ثبت نام کردید هستید.", "welcome.cta": "برای تأیید آدرس ایمیل خود اینجا کلیک کنید", - "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", - "reset.text2": "To continue with the password reset, please click on the following link:", + "reset.text1": "ما یک درخواست برای بازنشانی رمزعبور شما دریافت کرده ایم، احتمالا به این دلیل که شما آن را فراموش کرده اید. اگر این مورد نیست و شما رمز خود را به یاد دارید، لطفا این ایمیل را نادیده بگیرید.", + "reset.text2": "برای ادامه بازنشانی رمز، لطفابر روی این لینک کلیک کنید:", "reset.cta": "برای تنظیم مجدد گذرواژه‌ی خود اینجا کلیک کنید", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", - "digest.notifications": "You have unread notifications from %1:", - "digest.latest_topics": "Latest topics from %1", + "reset.notify.subject": "گذرواژه با موفقیت تغییر کرد", + "reset.notify.text1": "به شما اعلام میداریم که در %1، گذرواژه شما با موفقیت بازنشانی شد.", + "reset.notify.text2": "اگر این را تایید نمیکنید، لطفا بلافاصله به یک مدیر اطلاع دهید.", + "digest.notifications": "شما دارای اطلاعیه نخوانده ای از %1 هستید: ", + "digest.latest_topics": "آخرین دیدگاه های %1:", "digest.cta": "برای دیدن 1% اینجا کلیک کنید", - "digest.unsub.info": "This digest was sent to you due to your subscription settings.", - "digest.no_topics": "There have been no active topics in the past %1", - "notif.chat.subject": "New chat message received from %1", + "digest.unsub.info": "این اعداد که برای شما فرستاده شده به علت تنظیمات اشترک شماست.", + "digest.no_topics": "در %1 گذشته هیچ جستاری فعال نبوده است", + "notif.chat.subject": "پیام گفتگوی جدیدی از %1 دریافت شد", "notif.chat.cta": "برای ادامه‌ی گفتگو اینجا کلیک کنید", - "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", - "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", + "notif.chat.unsub.info": "این اطلاعیه ی گفتگویی که برای شما فرستاده شده به علت تنظیمات اشترک شماست.", + "notif.post.cta": "برای مشاهده کامل جستار اینجا کلوک کنید", + "notif.post.unsub.info": "این اطلاعیه ی دیدگاهی که برای شما فرستاده شده به علت تنظیمات اشترک شماست.", + "test.text1": "این یک ایمیل امتحانی جهت تایید اینکه فرستنده ایمیل برای انجمن NodeBB شما به درستی تنظیم و نصب شده است", "unsub.cta": "برای ویرایش آن تنظیمات اینجا کلیک کنید", "closing": "سپاس!" } \ No newline at end of file diff --git a/public/language/fa_IR/error.json b/public/language/fa_IR/error.json index cc32d1873e..b7e7237c4a 100644 --- a/public/language/fa_IR/error.json +++ b/public/language/fa_IR/error.json @@ -12,70 +12,72 @@ "invalid-title": "عنوان نامعتبر است!", "invalid-user-data": "داده‌های کاربری نامعتبر است.", "invalid-password": "گذرواژه نامعتبر است.", - "invalid-username-or-password": "Please specify both a username and password", - "invalid-search-term": "Invalid search term", + "invalid-username-or-password": "لطفا هم نام کاربری و هم کلمه عبور را مشخص کنید", + "invalid-search-term": "کلمه جستجو نامعتبر است", "invalid-pagination-value": "عدد صفحه‌بندی نامعتبر است.", "username-taken": "این نام کاربری گرفته شده است.", "email-taken": "این رایانامه گرفته شده است.", - "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "email-not-confirmed": "پست الکترونیک شما تاکنون تایید نشده است، برای تایید ایمیل خود را اینجا را کلیک کنید.", + "email-not-confirmed-chat": "شما تا قبل از تایید رایانامه قادر به گفتگو نیستید، لطفا برای تایید رایانامه خود اینجا کلیک کنید", + "no-email-to-confirm": "این انجمن نیاز به تایید رایانامه دارد، لطفا برای وارد کردن رایانامه اینجا کلیک کنید", + "email-confirm-failed": "ما نتوانستیم رایانامه شما را تایید کنیم، لطفا بعدا دوباره سعی کنید", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "نام کاربری خیلی کوتاه است.", - "username-too-long": "Username too long", + "username-too-long": "نام کاربری بسیار طولانیست", "user-banned": "کاربر محروم شد.", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "دسته بندی وجود ندارد", - "no-topic": "Topic does not exist", - "no-post": "Post does not exist", - "no-group": "Group does not exist", - "no-user": "User does not exist", - "no-teaser": "Teaser does not exist", - "no-privileges": "You do not have enough privileges for this action.", - "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", + "no-topic": "جستار وجود ندارد.", + "no-post": "دیدگاه وجود ندارد", + "no-group": "گروه وجود ندارد", + "no-user": "کاربر وجود ندارد", + "no-teaser": "تیزر وجود ندارد", + "no-privileges": "شما دسترسی کافی برای این کار را ندارید", + "no-emailers-configured": "افزونه ایمیلی بارگیری نشده است، پس رایانامه امتحانی نمیتواند فرستاده شود", "category-disabled": "دسته غیر‌فعال شد.", "topic-locked": "جستار بسته شد.", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "خواهشمندیم تا پایان بارگذاری‌ها شکیبا باشید.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "خواهشمندیم عنوان کوتاه‌تری بنویسید. عنوان‌ها نمی‌توانند بیش‌تر از %1 نویسه داشته باشند.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "شما نمی‌توانید به دیدگاه خود رای بدهید.", - "already-favourited": "You have already favourited this post", - "already-unfavourited": "You have already unfavourited this post", + "already-favourited": "شما قبلا این دیدگاه را محبوب کرده اید", + "already-unfavourited": "شما قبلا این دیدگاه را نامحبوب کرده اید", "cant-ban-other-admins": "شما نمی‌توانید دیگر مدیران را محروم کنید!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "invalid-image-type": "نوع تصویر نامعتبر است. نوعهای قابل قبول اینها هستند: %1", + "invalid-image-extension": "پسوند عکس نامعتبر است", + "invalid-file-type": "نوع پرونده نامعتبر است. نوعهای قابل قبول اینها هستند: %1", "group-name-too-short": "نام گروه خیلی کوتاه است.", "group-already-exists": "این گروه از پیش وجود دارد.", "group-name-change-not-allowed": "تغیر نام گروه نیاز به دسترسی دارد.", - "group-already-member": "You are already part of this group", - "group-needs-owner": "This group requires at least one owner", - "post-already-deleted": "This post has already been deleted", - "post-already-restored": "This post has already been restored", - "topic-already-deleted": "This topic has already been deleted", - "topic-already-restored": "This topic has already been restored", + "group-already-member": "شما الان هم عضوی از این گروه هستید", + "group-needs-owner": "این گروه حداقل یک مالک باید داشته باشد", + "post-already-deleted": "این دیدگاه پیش‌تر پاک شده است", + "post-already-restored": "دیدگاه پیش‌تر بازگردانی شده است.", + "topic-already-deleted": "جستار پیش‌تر حذف شده است", + "topic-already-restored": "جستار پیش‌تر بازگردانی شده است", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "چهرک‌های جستار غیرفعال شده است.", "invalid-file": "فایل نامعتبر است.", "uploads-are-disabled": "امکان بارگذاری غیرفعال شده است.", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "شما نمی‌توانید با خودتان گفتگو کنید!", - "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", - "too-many-messages": "You have sent too many messages, please wait awhile.", - "reputation-system-disabled": "Reputation system is disabled.", - "downvoting-disabled": "Downvoting is disabled", + "chat-restricted": "این کاربر پیام های گفتگوی خود را محدود کرده است . آنها بایدشما را دنبال کنند تا اینکه شما بتوانید به آنها پیامی بفرستید", + "too-many-messages": "شما پیامهای خیلی زیادی فرستاده اید، لطفا مدتی صبر نمایید", + "reputation-system-disabled": "سیستم اعتبار غیر فعال شده است", + "downvoting-disabled": "رای منفی غیر فعال شده است", "not-enough-reputation-to-downvote": "شما اعتبار کافی برای دادن رای منفی به این دیدگاه را ندارید.", - "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", - "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", - "registration-error": "Registration Error", - "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "not-enough-reputation-to-flag": "شما اعتبار کافی برای نشاندار کردن این دیدگاه ندارید", + "reload-failed": "NodeBB در هنگام بارگذاری مجدد با یک مشکل مواجه شده است: \"%1\". NodeBB سرویس رسانی به کلاینت های سرویس گیرنده را ادامه خواهد داد، اگرچه شما کاری را قبل از بارگیری مجدد انجام دادید بازگردانی کنید", + "registration-error": "خطای ثبت نام", + "parse-error": "هنگام تجزیه پاسخ سرور اشتباهی پیش امد", + "wrong-login-type-email": "لطفا از رایانامه خود برای ورود استفاده کنید", + "wrong-login-type-username": "لطفا از نام کاربری خود برای ورود استفاده کنید" } \ No newline at end of file diff --git a/public/language/fa_IR/global.json b/public/language/fa_IR/global.json index 6ef5ffe217..b68c1d212d 100644 --- a/public/language/fa_IR/global.json +++ b/public/language/fa_IR/global.json @@ -3,10 +3,10 @@ "search": "جستجو", "buttons.close": "بستن", "403.title": "دسترسی ندارید", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", - "403.login": "Perhaps you should try logging in?", + "403.message": "به نظر میاید شما به صفحه ای برخورد کرده اید که دسترسی به آن ندارید.", + "403.login": "شاید باید وارد شوید؟", "404.title": "یافت نشد", - "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "404.message": "به نظر میاید شما به صفحه ای برخورد کرده اید که وجود ندارد. بازگشت به صفحه ی خانه", "500.title": "خطای درونی.", "500.message": "اوه! گویا اشتباهی رخ داده!", "register": "نام‌نویسی", @@ -27,7 +27,7 @@ "header.tags": "برچسب‌ها", "header.popular": "دوست‌داشتنی‌ها", "header.users": "کاربران", - "header.groups": "Groups", + "header.groups": "گروه ها", "header.chats": "گفتگوها", "header.notifications": "آگاه‌سازی‌ها", "header.search": "جستجو", @@ -39,7 +39,7 @@ "nextpage": "برگهٔ پسین", "alert.success": "موفقیت", "alert.error": "خطا", - "alert.banned": "Banned", + "alert.banned": "بن شده ها", "alert.banned.message": "دسترسی شما مسدود شد. اکنون خارج خواهید شد.", "alert.unfollow": "شما دیگر %1 را دنبال نمی‌کنید!", "alert.follow": "اکنون %1 را دنبال می‌کنید.", @@ -74,8 +74,8 @@ "guests": "مهمان‌ها", "updated.title": "انجمن بروزرسانی شد", "updated.message": "این انجمن به آخرین نسخه بروزرسانی شد. برای بارگزاری مجدد صفحه اینجا کلیک کنید.", - "privacy": "Privacy", - "follow": "Follow", - "unfollow": "Unfollow", - "delete_all": "Delete All" + "privacy": "حریم خصوصی", + "follow": "دنبال کن", + "unfollow": "دنبال نکن", + "delete_all": "حذف همه" } \ No newline at end of file diff --git a/public/language/fa_IR/groups.json b/public/language/fa_IR/groups.json index d2314fdc29..f16a3f3200 100644 --- a/public/language/fa_IR/groups.json +++ b/public/language/fa_IR/groups.json @@ -1,34 +1,36 @@ { - "groups": "Groups", - "view_group": "View Group", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", - "details.title": "Group Details", - "details.members": "Member List", - "details.pending": "Pending Members", - "details.has_no_posts": "This group's members have not made any posts.", - "details.latest_posts": "Latest Posts", - "details.private": "Private", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "details.group_name": "Group Name", - "details.description": "Description", - "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", - "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", - "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", - "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "groups": "گروه ها", + "view_group": "مشاهده گروه", + "owner": "مالک گروه", + "new_group": "ساخت گروه جدید", + "no_groups_found": "گروهی برای دیدن وجود ندارد", + "pending.accept": "قبول", + "pending.reject": "رد", + "cover-instructions": "کشیدن و انداختن عکس، به محل بکشید، و ذخیره را بزنید", + "cover-change": "تغییر", + "cover-save": "ذخیره", + "cover-saving": "در حال ذخیره کردن", + "details.title": "جزئیات گروه", + "details.members": "لیست اعضا", + "details.pending": "اعضای در انتظار", + "details.has_no_posts": "اعضای این گروه هیچ دیدگاهی ایجاد نکرده اند", + "details.latest_posts": "آخرین دیدگاه ها", + "details.private": "خصوصی", + "details.grant": "اعطاء/خلع مالکیت", + "details.kick": "لگد", + "details.owner_options": "مدیر گروه", + "details.group_name": "نام گروه", + "details.member_count": "تعداد اعضا", + "details.creation_date": "زمان ساخته شدن", + "details.description": "توضیحات", + "details.badge_preview": "پیش نمایشِ نشان", + "details.change_icon": "تغییر آیکن", + "details.change_colour": "تغییر رنگ", + "details.badge_text": "نوشته ای نشان", + "details.userTitleEnabled": "نمایش نشان", + "details.private_help": "اگر فعال باشد، پیوستن به گروه مستلزم موافقت صاحب گروه است", + "details.hidden": "پنهان", + "details.hidden_help": "اگر فعال باشد، ایم گروه در فهرست گروه ها پیدا نمیشود، و کاربر باید دستی دعوت شود", + "event.updated": "جزییات گروه با موفقیت به روز گردید", + "event.deleted": "گروه \"%1\" حدف شد" } \ No newline at end of file diff --git a/public/language/fa_IR/login.json b/public/language/fa_IR/login.json index 07012aad28..7a7c99fd6b 100644 --- a/public/language/fa_IR/login.json +++ b/public/language/fa_IR/login.json @@ -1,7 +1,7 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username-email": "نام کاربری / رایانامه", + "username": "نام کاربری", + "email": "رایانامه", "remember_me": "مرا به یاد بسپار؟", "forgot_password": "گذرواژه را فراموش کرده‌اید؟", "alternative_logins": "روش‌های درون آمدن جایگزین", diff --git a/public/language/fa_IR/modules.json b/public/language/fa_IR/modules.json index 0456ff2842..81b4fab4fc 100644 --- a/public/language/fa_IR/modules.json +++ b/public/language/fa_IR/modules.json @@ -10,13 +10,17 @@ "chat.recent-chats": "گفتگوهای اخیر", "chat.contacts": "تماس‌ها", "chat.message-history": "تاریخچه پیام‌ها", - "chat.pop-out": "Pop out chat", + "chat.pop-out": "پاپ آپ گفتگو", "chat.maximize": "تمام صفحه", - "chat.seven_days": "7 Days", - "chat.thirty_days": "30 Days", - "chat.three_months": "3 Months", + "chat.seven_days": "7 روز", + "chat.thirty_days": "30 روز", + "chat.three_months": "3 ماه", + "composer.compose": "ارسال", + "composer.show_preview": "نمایش پیش‌نمایش", + "composer.hide_preview": "مخفی کردن پیش‌نمایش", "composer.user_said_in": "%1 در %2 گفته است:", "composer.user_said": "%1 گفته است:", "composer.discard": "آیا از دور انداختن این دیدگاه اطمینان دارید؟", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "ارسال و قفل", + "composer.toggle_dropdown": "باز و بسته کردن کرکره" } \ No newline at end of file diff --git a/public/language/fa_IR/notifications.json b/public/language/fa_IR/notifications.json index e56553bba6..0775c24103 100644 --- a/public/language/fa_IR/notifications.json +++ b/public/language/fa_IR/notifications.json @@ -2,24 +2,24 @@ "title": "آگاه‌سازی‌ها", "no_notifs": "هیچ آگاه‌سازی تازه‌ای ندارید", "see_all": "دیدن همهٔ آگاه‌سازی‌ها", - "mark_all_read": "Mark all notifications read", + "mark_all_read": "همه اطلاعیه ها را خوانده شده علامت بزن", "back_to_home": "بازگشت به %1", "outgoing_link": "پیوند برون‌رو", - "outgoing_link_message": "You are now leaving %1.", - "continue_to": "Continue to %1", - "return_to": "Return to %1", + "outgoing_link_message": "در حال ترک %1 هستید.", + "continue_to": "ادامه به %1", + "return_to": "بازگشت به 1%", "new_notification": "آکاه‌سازی تازه", "you_have_unread_notifications": "شما آگاه‌سازی‌های نخوانده دارید.", "new_message_from": "پیام تازه از %1", - "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", - "favourited_your_post_in": "%1 has favourited your post in %2.", - "user_flagged_post_in": "%1 flagged a post in %2", + "upvoted_your_post_in": "%1 امتیاز مثبت به دیدگاه شما در %2 داده", + "moved_your_post": "%1دیدگاه شما را منتقل کرده.", + "moved_your_topic": "%1جستار شما را منتقل کرده.", + "favourited_your_post_in": "%1 دیدگاه شما را در %2 برگزیده کرده.", + "user_flagged_post_in": "%1 دیدگاه شما را در %2 علامتدار کرده", "user_posted_to": "پاسخ دادن به %2 از سوی %1", - "user_posted_topic": "%1 has posted a new topic: %2", - "user_mentioned_you_in": "%1 mentioned you in %2", - "user_started_following_you": "%1 started following you.", + "user_posted_topic": "%1 یک جستار جدید ارسال کرده: %2", + "user_mentioned_you_in": "%1 در \n%1 mentioned you in %2 از شما نام برده", + "user_started_following_you": "%1 شروع به دنبال کردن شما کرده", "email-confirmed": "رایانامه تایید شد", "email-confirmed-message": "بابت تایید ایمیلتان سپاس‌گزاریم. حساب کاربری شما اکنون به صورت کامل فعال شده است.", "email-confirm-error-message": "خطایی در تایید آدرس ایمیل شما پیش آمده است. ممکن است کد نا‌معتبر و یا منقضی شده باشد.", diff --git a/public/language/fa_IR/pages.json b/public/language/fa_IR/pages.json index 0daf56c95b..18fa275e86 100644 --- a/public/language/fa_IR/pages.json +++ b/public/language/fa_IR/pages.json @@ -5,16 +5,17 @@ "recent": "جستارهای تازه", "users": "کاربران نام‌نویسی شده", "notifications": "آگاه‌سازی‌ها", - "tags": "Topics tagged under \"%1\"", + "tags": "برچسب‌ها", + "tag": "جستارهای داری کلیدواژه \"%1\"", "user.edit": "ویرایش \"%1\"", - "user.following": "%1 کاربر دنبال می‌کنند", + "user.following": "کاربرانی که %1 دنبال می‌کند", "user.followers": "کاربرانی که %1 را دنبال می‌کنند", "user.posts": "دیدگاه‌های %1", "user.topics": "%1 این جستار را ساخت.", - "user.groups": "%1's Groups", + "user.groups": "گروه های %1", "user.favourites": "دیدگاه‌های پسندیدهٔ %1", "user.settings": "تنظیمات کاربر", - "user.watched": "Topics watched by %1", - "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", - "maintenance.messageIntro": "Additionally, the administrator has left this message:" + "user.watched": "جستارهای پاییده شده توسط %1", + "maintenance.text": "1% در حال حاضر تحت تعمیر و نگهدارییست. لطفا زمان دیگری مراجعه کنید.", + "maintenance.messageIntro": "علاوه بر این، مدیر این پیام را گذاشته است:" } \ No newline at end of file diff --git a/public/language/fa_IR/recent.json b/public/language/fa_IR/recent.json index 2a9060fad9..8000095b63 100644 --- a/public/language/fa_IR/recent.json +++ b/public/language/fa_IR/recent.json @@ -4,16 +4,16 @@ "week": "هفته", "month": "ماه", "year": "سال", - "alltime": "All Time", - "no_recent_topics": "There are no recent topics.", - "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "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 دیدگاه جدید موجود است.", + "click-here-to-reload": "برای بازگذاری مجدد کلیک کنید." } \ No newline at end of file diff --git a/public/language/fa_IR/reset_password.json b/public/language/fa_IR/reset_password.json index 5e6ea61461..b587fb85ed 100644 --- a/public/language/fa_IR/reset_password.json +++ b/public/language/fa_IR/reset_password.json @@ -11,6 +11,7 @@ "enter_email_address": "نوشتن نشانی رایانامه", "password_reset_sent": "رایانامهٔ بازیابی گذرواژه فرستاده شد", "invalid_email": "رایانامهٔ نامعتبر / رایانامه وجود ندارد!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "گذرواژه وارد شده خیلی کوتاه است، لطفا یک گذر واژه طولانی تر انتخاب کنید.", + "passwords_do_not_match": "دو گذرواژه ای که وارد کرده اید مطابقت ندارند.", + "password_expired": "گذرواژه شما منقضی شده، لطفا گذرواژه جدیدی انتخاب کنید" } \ No newline at end of file diff --git a/public/language/fa_IR/search.json b/public/language/fa_IR/search.json index 9dad8b6eab..52bcdc087a 100644 --- a/public/language/fa_IR/search.json +++ b/public/language/fa_IR/search.json @@ -1,40 +1,40 @@ { - "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", - "no-matches": "No matches found", - "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order", - "save-preferences": "Save preferences", - "clear-preferences": "Clear preferences", - "search-preferences-saved": "Search preferences saved", - "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "results_matching": "%1 نتیجه (ها) مطابق با \"%2\" ,(%3 ثانیه)", + "no-matches": "هیچ موردی یافت نشد", + "advanced-search": "جستجوی پیشرفته", + "in": "در", + "titles": "عناوین", + "titles-posts": "عناوین و دیدگاه ها", + "posted-by": "ارسال شده توسط", + "in-categories": "در دسته ها", + "search-child-categories": "جستجوی دسته های زیر شاخه", + "reply-count": "تعداد پاسخ", + "at-least": "حداقل", + "at-most": "حداکثر", + "post-time": "زمان ارسال", + "newer-than": "جدیدتر از", + "older-than": "قدیمی تر از", + "any-date": "هر زمانی", + "yesterday": "دیروز", + "one-week": "یک هفته", + "two-weeks": "دو هفته", + "one-month": "یک ماه", + "three-months": "سه ماه", + "six-months": "شش ماه", + "one-year": "یک سال", + "sort-by": "مرتب‌سازی بر اساس", + "last-reply-time": "زمان آخرین پاسخ", + "topic-title": "عنوان جستار", + "number-of-replies": "تعداد پاسخها", + "number-of-views": "تعداد مشاهده ها", + "topic-start-date": "زمان شروع جستار", + "username": "نام کاربری", + "category": "دسته", + "descending": "به ترتیب نزولی", + "ascending": "به ترتیب سعودی", + "save-preferences": "ذخیره تنظیمات", + "clear-preferences": "پاک کردن تنظیمات", + "search-preferences-saved": "تنظیمات جستحو ذخیره شد", + "search-preferences-cleared": "تنظیمات جستجو پاک شد", + "show-results-as": "نمایش نتایج به عنوان" } \ No newline at end of file diff --git a/public/language/fa_IR/tags.json b/public/language/fa_IR/tags.json index 26a393396b..c373aaa4ad 100644 --- a/public/language/fa_IR/tags.json +++ b/public/language/fa_IR/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "جُستاری با این برچسب وجود ندارد.", "tags": "برچسب‌ها", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", - "enter_tags_here_short": "Enter tags...", + "enter_tags_here": "برچسب‌ها را اینجا وارد کنید. هر کدام بین %1 و %2 نویسه", + "enter_tags_here_short": "برچسب ها را وارد کنید ...", "no_tags": "هنوز برچسبی وجود ندارد." } \ No newline at end of file diff --git a/public/language/fa_IR/topic.json b/public/language/fa_IR/topic.json index f523bee5b6..2ed30ffa38 100644 --- a/public/language/fa_IR/topic.json +++ b/public/language/fa_IR/topic.json @@ -12,9 +12,9 @@ "notify_me": "از پاسخ‌های تازه در جستار آگاه شوید", "quote": "نقل قول", "reply": "پاسخ", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "وارد شوید تا دیدگاه بفرستید", "edit": "ویرایش", - "delete": "Delete", + "delete": "حذف", "purge": "پاک کردن", "restore": "برگرداندن", "move": "جابه‌جا کردن", @@ -28,17 +28,17 @@ "flag_title": "پرچم‌گذاری این جستار برای بررسی ناظران", "flag_confirm": "آیا مطمئنید که می‌خواهید روی این دیدگاه پرچم بگذارید.", "flag_success": "این جستار برای بررسی ناظران پرچم گذاشته شد.", - "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", + "deleted_message": "این جستار پاک شده است. تنها کاربرانِ با حق مدیریت جستار می‌توانند آن را ببینند.", "following_topic.message": "از این پس اگر کسی در این جستار دیدگاه بگذارد، شما آگاه خواهید شد.", "not_following_topic.message": "شما دیگر آگاه‌سازی‌های این جستار را دریافت نخواهید کرد.", "login_to_subscribe": "برای دنبال کردن این جستار، لطفا نام‌نویسی کنید یا به درون بیایید.", "markAsUnreadForAll.success": "جستار برای همگان نخوانده در نظر گرفته شد.", - "watch": "تماشا کردن", - "unwatch": "Unwatch", + "watch": "پاییدن", + "unwatch": "نپاییدن", "watch.title": "از پاسخ‌های تازه به این جستار آگاه شوید.", - "unwatch.title": "Stop watching this topic", + "unwatch.title": "توقف پاییدن این جستار", "share_this_post": "به اشتراک‌گذاری این جستار", - "thread_tools.title": "Topic Tools", + "thread_tools.title": "ابزارهای جستار", "thread_tools.markAsUnreadForAll": "نخوانده بگیر", "thread_tools.pin": "سنجاق زدن جستار", "thread_tools.unpin": "برداشتن سنجاق جستار", @@ -48,11 +48,11 @@ "thread_tools.move_all": "جابجایی همه", "thread_tools.fork": "شاخه ساختن از جستار", "thread_tools.delete": "پاک کردن جستار", - "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", + "thread_tools.delete_confirm": "آیا مطمئنید می خواهید این جستار را حذف کنید؟", "thread_tools.restore": "برگرداندن جستار", - "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", + "thread_tools.restore_confirm": "آیا مطمئنید که می خواهید این جستار را بازگردانی کنید؟", "thread_tools.purge": "پاک کردن جستار", - "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", + "thread_tools.purge_confirm": "آیا مطمئنید که می خواهید این حستار را پاکسازی کنید؟", "topic_move_success": "جابه‌جایی این جستار به %1 باموفقیت انجام شد.", "post_delete_confirm": "آیا از پاک کردن این دیدگاه اطمینان دارید؟", "post_restore_confirm": "آیا از بازگردانی این دیدگاه اطمینان دارید؟", @@ -73,9 +73,9 @@ "topic_will_be_moved_to": "این جستار جابه‌جا خواهد شد به دستهٔ", "fork_topic_instruction": "دیدگاه‌هایی را که می‌خواهید به جستار تازه ببرید، برگزینید", "fork_no_pids": "هیچ دیدگاهی انتخاب نشده!", - "fork_success": "Successfully forked topic! Click here to go to the forked topic.", + "fork_success": "موضوع با موفقیت منشعب شد! برای رفتن به موضوع انشعابی اینجا را کلیک کنید.", "composer.title_placeholder": "سرنویس جستارتان را اینجا بنویسید...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "نام", "composer.discard": "دور بیانداز", "composer.submit": "بفرست", "composer.replying_to": "پاسخ به %1", @@ -90,10 +90,10 @@ "more_users_and_guests": "%1 کاربر() و %2 مهمان()", "more_users": "1% کاربر()", "more_guests": "1% مهمان()", - "users_and_others": "%1 and %2 others", + "users_and_others": "%1 و %2 دیگر", "sort_by": "مرتب‌سازی بر اساس", "oldest_to_newest": "قدیمی‌ترین به جدید‌ترین", "newest_to_oldest": "جدید‌ترین به قدیمی‌ترین", "most_votes": "بیشترین رای‌ها", - "most_posts": "Most posts" + "most_posts": "بیشتر دیدگاه ها" } \ No newline at end of file diff --git a/public/language/fa_IR/user.json b/public/language/fa_IR/user.json index c2b90d3c4c..e3722d684c 100644 --- a/public/language/fa_IR/user.json +++ b/public/language/fa_IR/user.json @@ -1,13 +1,13 @@ { - "banned": "مسدود", + "banned": "اخراج شده", "offline": "آفلاین", "username": "نام کاربری", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "زمان عضویت", + "postcount": "تعداد دیدگاه ها", "email": "رایانامه", "confirm_email": "تأیید رایانامه", - "delete_account": "Delete Account", - "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_account": "حذف حساب کاربری", + "delete_account_confirm": "آیا مطمئنید که میخواهید حساب کاربری خود را حذف کنید؟
این عمل غیر قابل بازگشت است و شما قادر نخواهید بود هیچ کدام از اطلاعات خود را بازیابی کنید./strong>

برای تایید حذف این حساب کاربری، نام کاربری خود را وارد کنید", "fullname": "نام کامل", "website": "تارنما", "location": "محل سکونت", @@ -18,7 +18,7 @@ "profile_views": "بازدیدهای نمایه", "reputation": "اعتبار", "favourites": "پسندها", - "watched": "Watched", + "watched": "پاییده شده", "followers": "دنبال‌کننده‌ها", "following": "دنبال‌شونده‌ها", "signature": "امضا", @@ -27,12 +27,13 @@ "chat": "گفتگو", "follow": "دنبال کن", "unfollow": "دنبال نکن", + "more": "بیشتر", "profile_update_success": "نمایه باموفقیت به روز شده است!", "change_picture": "تغییر تصویر", "edit": "ویرایش", "uploaded_picture": "تصویر بارشده", "upload_new_picture": "بارگذاری تصویر تازه", - "upload_new_picture_from_url": "Upload New Picture From URL", + "upload_new_picture_from_url": "بارگذاری تصویر جدید از نشانی وب", "current_password": "گذرواژه کنونی", "change_password": "تغیر گذرواژه", "change_password_error": "گذرواژهٔ نامعتبر!", @@ -47,36 +48,36 @@ "upload_picture": "بارگذاری تصویر", "upload_a_picture": "یک تصویر بارگذاری کنید", "image_spec": "شما تنها می‌توانید پرونده‌های PNG‏، JPG و GIF بار بگذارید.", - "max": "بیشینه", "settings": "تنظیمات", "show_email": "نمایش رایانامه‌ام", "show_fullname": "نام کامل من را نشان بده", - "restrict_chats": "Only allow chat messages from users I follow", + "restrict_chats": "قبول پیغام فقط ازکاربرانی که من را دنبال میکنند", "digest_label": "مشترک شدن در چکیده", "digest_description": "مشترک شدن برای دریافت تازه‌هی این انجمن (جستارها و آکاه‌سازی‌های تازه) با رایانامه روی یک برنامه زمان‌بندی", "digest_off": "خاموش", "digest_daily": "روزانه", "digest_weekly": "هفتگی", "digest_monthly": "ماهانه", - "send_chat_notifications": "Send an email if a new chat message arrives and I am not online", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "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": "This user didn't watch any topics yet.", + "has_no_watched_topics": "این کاربر هنوز هیچ جستاری را نپاییده است.", "email_hidden": "رایانامه پنهان شده", "hidden": "پنهان", - "paginate_description": "محدود کردن شمار جستارها و دیدگاه‌ها در هر برگه به جای مرور بی‌پایان برگه‌ها", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "شمار جستارها در هر برگه", "posts_per_page": "شمار دیدگاه‌ها در هر برگه", - "notification_sounds": "پخش صدا هنگامی که شما یک آگاه‌سازی دریافت می‌کنید.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "تنظیمات مرور", - "open_links_in_new_tab": "بازکردن لینک‌های خارجی در تب جدید؟", - "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "open_links_in_new_tab": "Open outgoing links in new tab", + "enable_topic_searching": "فعال کردن جستجوی داخل-جستار ", + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "عنوان گروهی که میخواهید نشان داده شود را انتخاب کنید.", + "no-group-title": "عنوان گروه ای نیست" } \ No newline at end of file diff --git a/public/language/fa_IR/users.json b/public/language/fa_IR/users.json index b164bd0839..c313b79dd8 100644 --- a/public/language/fa_IR/users.json +++ b/public/language/fa_IR/users.json @@ -5,8 +5,8 @@ "search": "جستجو", "enter_username": "یک نام کاربری برای جستجو وارد کنید", "load_more": "بارگذاری بیش‌تر", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "users-found-search-took": "%1 کاربر(ها) یافت شد! جستجو %2 ثانیه طولید", + "filter-by": "غربال با", + "online-only": "فقط آنلاین", + "picture-only": "عکس فقط" } \ No newline at end of file diff --git a/public/language/fi/category.json b/public/language/fi/category.json index 6e36ca410c..0c0233c3b1 100644 --- a/public/language/fi/category.json +++ b/public/language/fi/category.json @@ -5,5 +5,8 @@ "browsing": "selaamassa", "no_replies": "Kukaan ei ole vastannut", "share_this_category": "Jaa tämä kategoria", - "ignore": "Sivuuta" + "watch": "Watch", + "ignore": "Sivuuta", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/fi/error.json b/public/language/fi/error.json index c343ca1345..6001a3f211 100644 --- a/public/language/fi/error.json +++ b/public/language/fi/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Käyttäjänimi on liian lyhyt", "username-too-long": "Käyttäjänimi on liian pitkä", "user-banned": "Käyttäjä on estetty", - "user-too-new": "Pahottelut, joudut odottamaan %1 sekuntia ennen ensimmäisen viestin kirjoittamista", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategoriaa ei ole olemassa", "no-topic": "Aihetta ei ole olemassa", "no-post": "Viestiä ei ole olemassa", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategoria ei ole käytössä", "topic-locked": "Aihe lukittu", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Ole hyvä ja odota tiedostojen lähettämisen valmistumista.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Otsikkosi on liian pitkä. Otsikoiden pituuden tulee olla enintään %1 merkkiä.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Suurin sallittu tiedostokoko on %1 kbs - ole hyvä ja lataa pienempi tiedosto", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Et voi antaa ääntä omalle viestillesi", "already-favourited": "Tämä viesti on jo suosikeissasi", "already-unfavourited": "Olet jo poistanut tämän viestin suosikeistasi", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Aiheiden kuvakkeet eivät ole käytössä", "invalid-file": "Virheellinen tiedosto", "uploads-are-disabled": "Et voi lähettää tiedostoa", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Et voi keskustella itsesi kanssa!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/fi/groups.json b/public/language/fi/groups.json index c8538e2c28..b320947545 100644 --- a/public/language/fi/groups.json +++ b/public/language/fi/groups.json @@ -20,6 +20,8 @@ "details.kick": "Potkaise", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/fi/modules.json b/public/language/fi/modules.json index 7823638f1b..a789f8dd91 100644 --- a/public/language/fi/modules.json +++ b/public/language/fi/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 päivää", "chat.thirty_days": "30 päivää", "chat.three_months": "3 kuukautta", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 sanoi:", "composer.discard": "Oletko varma, että haluat hylätä viestin?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/fi/pages.json b/public/language/fi/pages.json index d2fc4993d6..c47769bd90 100644 --- a/public/language/fi/pages.json +++ b/public/language/fi/pages.json @@ -5,7 +5,8 @@ "recent": "Viimeisimmät aiheet", "users": "Rekisteröityneet käyttäjät", "notifications": "Ilmoitukset", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Muokataan \"%1\"", "user.following": "Käyttäjät, joita %1 seuraa", "user.followers": "Käyttäjät, jotka seuraavat käyttäjää %1", diff --git a/public/language/fi/reset_password.json b/public/language/fi/reset_password.json index 9b523e7e2b..5836ce801b 100644 --- a/public/language/fi/reset_password.json +++ b/public/language/fi/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Salasanan palautuskoodi lähetetty", "invalid_email": "Virheellinen sähköpostiosoite / Sähköpostiosoitetta ei ole olemassa!", "password_too_short": "Salasana on liian lyhyt, käytä pidempää salasanaa.", - "passwords_do_not_match": "Salasana ja sen vahvistus eivät täsmää." + "passwords_do_not_match": "Salasana ja sen vahvistus eivät täsmää.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/fi/search.json b/public/language/fi/search.json index df7f7a2352..d920c18409 100644 --- a/public/language/fi/search.json +++ b/public/language/fi/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Otsikot", "titles-posts": "Otsikot ja Viestit", "posted-by": "Kirjoittanut", diff --git a/public/language/fi/user.json b/public/language/fi/user.json index 771ea13132..3a4c176679 100644 --- a/public/language/fi/user.json +++ b/public/language/fi/user.json @@ -27,6 +27,7 @@ "chat": "Keskustele", "follow": "Seuraa", "unfollow": "Älä seuraa", + "more": "More", "profile_update_success": "Profiili päivitettiin onnistuneesti!", "change_picture": "Vaihda kuva", "edit": "Muokkaa", @@ -47,7 +48,6 @@ "upload_picture": "Lataa kuva", "upload_a_picture": "Lataa kuva", "image_spec": "Voit ladata vain PNG-, JPG- tai GIF-tiedostoja", - "max": "max.", "settings": "Asetukset", "show_email": "Näytä sähköpostiosoitteeni", "show_fullname": "Näytä koko nimeni", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Tämä käyttäjä ei seuraa vielä yhtään aihetta.", "email_hidden": "Sähköposti piilotettu", "hidden": "piilotettu", - "paginate_description": "Sivuta aiheet ja viestit loputtoman vierittämisen sijaan.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Aihetta per sivu", "posts_per_page": "Viestiä per sivu", - "notification_sounds": "Soita merkkiääni ilmoituksen saapuessa.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Selataan asetuksia", - "open_links_in_new_tab": "Avaa muualle vievät linkit uudessa välilehdessä?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Salli aiheen sisäiset haut", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Seuraa aiheita, joihin olen vastannut.", - "follow_topics_you_create": "Seuraa aloittamiani aiheita.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/fr/category.json b/public/language/fr/category.json index 98a8ae2b90..0e0a238c35 100644 --- a/public/language/fr/category.json +++ b/public/language/fr/category.json @@ -5,5 +5,8 @@ "browsing": "parcouru par", "no_replies": "Personne n'a répondu", "share_this_category": "Partager cette catégorie", - "ignore": "Ignorer" + "watch": "Suivre", + "ignore": "Ignorer", + "watch.message": "Vous suivez désormais les mises à jour de cette catégorie.", + "ignore.message": "Vous ne suivez plus les mises jour de cette catégorie." } \ No newline at end of file diff --git a/public/language/fr/error.json b/public/language/fr/error.json index b2892fde19..880c9ce132 100644 --- a/public/language/fr/error.json +++ b/public/language/fr/error.json @@ -12,19 +12,20 @@ "invalid-title": "Titre invalide !", "invalid-user-data": "Données utilisateur invalides", "invalid-password": "Mot de passe invalide", - "invalid-username-or-password": "S'il vous plait, veuillez entrer un nom d'utilisateur et un mot de passe", + "invalid-username-or-password": "Veuillez entrer un nom d'utilisateur et un mot de passe", "invalid-search-term": "Données de recherche invalides", "invalid-pagination-value": "Valeur de pagination invalide", "username-taken": "Nom d’utilisateur déjà utilisé", "email-taken": "Email déjà utilisé", "email-not-confirmed": "Votre adresse email n'est pas confirmée, cliquez ici pour la valider.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "Il ne vous est pas possible d'utiliser le chat tant que votre adresse email n'a pas été vérifiée. Veuillez cliquer ici pour confirmer votre adresse email.", "no-email-to-confirm": "Ce forum requiert une vérification de votre adresse email. Veuillez cliquer ici pour entrer une adresse.", "email-confirm-failed": "Votre adresse email n'a pas pu être vérifiée. Veuillez ré-essayer plus tard.", + "confirm-email-already-sent": "L'email de confirmation a déjà été envoyé. Veuillez attendre %1 minute(s) avant de redemander un nouvel envoi.", "username-too-short": "Nom d'utilisateur trop court", "username-too-long": "Nom d'utilisateur trop long", "user-banned": "Utilisateur banni", - "user-too-new": "Désolé, vous devez attendre encore %1 secondes avant d'envoyer votre premier message", + "user-too-new": "Désolé, vous devez attendre encore %1 seconde(s) avant d'envoyer votre premier message", "no-category": "Cette catégorie n'existe pas", "no-topic": "Ce sujet n'existe pas", "no-post": "Ce message n'existe pas", @@ -35,17 +36,17 @@ "no-emailers-configured": "Un email de test n'a pas pu être envoyé car aucun plugin de gestion des emails n'était chargé", "category-disabled": "Catégorie désactivée", "topic-locked": "Sujet verrouillé", - "post-edit-duration-expired": "Vous ne pouvez modifier un post que %1 secondes après l'avoir posté.", + "post-edit-duration-expired": "Vous ne pouvez modifier un message que %1 seconde(s) après l'avoir posté.", "still-uploading": "Veuillez patienter pendant le téléchargement.", - "content-too-short": "Veuillez entrer un message plus long. %1 caractères minimum.", - "content-too-long": "Veuillez poster un message plus cours. Les messages ne peuvent être plus long que %1 caractères.", - "title-too-short": "Veuillez entrer un titre plus long. %1 caractères minimum.", - "title-too-long": "Veuillez entrer un titre plus court. Les titres ne peuvent excéder %1 caractères.", - "too-many-posts": "Vous ne pouvez poster que toutes les %1 secondes.", - "too-many-posts-newbie": "En tant que nouvel utilisateur, vous ne pouvez poster que toutes les %1 secondes jusqu'à ce que vous obteniez une réputation de %2 - patientez avant de publier de nouveau. ", - "tag-too-short": "Veuillez entrer un mot-clé plus long. Les mots-clés devraient contenir au moins %1 caractères.", - "tag-too-long": "Veuillez entrer un mot-clé plus court. Les mot-clés ne peuvent faire plus de %1 caractères.", - "file-too-big": "La taille maximum des fichiers est de %1 kbs.", + "content-too-short": "Veuillez entrer un message plus long. %1 caractère(s) minimum.", + "content-too-long": "Veuillez poster un message plus cours. Les messages ne peuvent être plus long que %1 caractère(s).", + "title-too-short": "Veuillez entrer un titre plus long. %1 caractère(s) minimum.", + "title-too-long": "Veuillez entrer un titre plus court. Les titres ne peuvent excéder %1 caractère(s).", + "too-many-posts": "Vous ne pouvez poster que toutes les %1 seconde(s).", + "too-many-posts-newbie": "En tant que nouvel utilisateur, vous ne pouvez poster que toutes les %1 seconde(s) jusqu'à ce que vous obteniez une réputation de %2 - patientez avant de publier de nouveau.", + "tag-too-short": "Veuillez entrer un mot-clé plus long. Les mots-clés doivent contenir au moins %1 caractère(s).", + "tag-too-long": "Veuillez entrer un mot-clé plus court. Les mot-clés ne peuvent faire plus de %1 caractère(s).", + "file-too-big": "La taille maximale autorisée pour un fichier est de %1 kb. Veuillez envoyer un fichier plus petit.", "cant-vote-self-post": "Vous ne pouvez pas voter pour vos propres messages", "already-favourited": "Vous avez déjà mis ce message en favoris", "already-unfavourited": "Vous avez déjà retiré ce message des favoris", @@ -62,10 +63,11 @@ "post-already-restored": "Message déjà restauré", "topic-already-deleted": "Sujet déjà supprimé", "topic-already-restored": "Sujet déjà restauré", + "cant-purge-main-post": "Il n'est pas possible d'effacer le message principal, veuillez supprimer le sujet entier à la place.", "topic-thumbnails-are-disabled": "Les miniatures de sujet sont désactivés", "invalid-file": "Fichier invalide", "uploads-are-disabled": "Les envois sont désactivés", - "signature-too-long": "La signature ne peut dépasser %1 caractères !", + "signature-too-long": "La signature ne peut dépasser %1 caractère(s).", "cant-chat-with-yourself": "Vous ne pouvez chatter avec vous même !", "chat-restricted": "Cet utilisateur a restreint les ses messages de chat. Il doit d'abord vous suivre avant de pouvoir discuter avec lui.", "too-many-messages": "Vous avez envoyé trop de messages, veuillez patienter un instant.", diff --git a/public/language/fr/groups.json b/public/language/fr/groups.json index ce980d472e..6ad4028954 100644 --- a/public/language/fr/groups.json +++ b/public/language/fr/groups.json @@ -20,6 +20,8 @@ "details.kick": "Exclure", "details.owner_options": "Administration du groupe", "details.group_name": "Nom du groupe", + "details.member_count": "Nombre de membres", + "details.creation_date": "Date de création", "details.description": "Description", "details.badge_preview": "Aperçu du badge", "details.change_icon": "Modifier l'icône", diff --git a/public/language/fr/modules.json b/public/language/fr/modules.json index 8589d50ca7..5c5e9b3fdf 100644 --- a/public/language/fr/modules.json +++ b/public/language/fr/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Jours", "chat.thirty_days": "30 Jours", "chat.three_months": "3 Mois", + "composer.compose": "Écrire", + "composer.show_preview": "Afficher l'aperçu", + "composer.hide_preview": "Masquer l'aperçu", "composer.user_said_in": "%1 a dit dans %2 :", "composer.user_said": "%1 a dit :", "composer.discard": "Êtes-vous sûr de bien vouloir supprimer ce message ?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Soumettre et Verrouiller", + "composer.toggle_dropdown": "Afficher/masquer le menu" } \ No newline at end of file diff --git a/public/language/fr/pages.json b/public/language/fr/pages.json index 9d57e1d68c..d78cf504d6 100644 --- a/public/language/fr/pages.json +++ b/public/language/fr/pages.json @@ -5,7 +5,8 @@ "recent": "Sujets récents", "users": "Utilisateurs enregistrés", "notifications": "Notifications", - "tags": "Sujets contenant le mot-clé \"%1\"", + "tags": "Mots-clés", + "tag": "Sujets ayant le mot-clé \"%1\"", "user.edit": "Edite \"%1\"", "user.following": "Personnes que %1 suit", "user.followers": "Personnes qui suivent %1", @@ -14,7 +15,7 @@ "user.groups": "Les Groupes de %1", "user.favourites": "Messages favoris de %1", "user.settings": "Préférences utilisateur", - "user.watched": "Topics watched by %1", + "user.watched": "Sujets surveillés par %1", "maintenance.text": "%1 est en maintenance. Veuillez revenir un peu plus tard.", "maintenance.messageIntro": "De plus, l'administrateur a laissé ce message:" } \ No newline at end of file diff --git a/public/language/fr/reset_password.json b/public/language/fr/reset_password.json index f21186c870..055aec4b60 100644 --- a/public/language/fr/reset_password.json +++ b/public/language/fr/reset_password.json @@ -11,6 +11,7 @@ "enter_email_address": "Entrer votre adresse email", "password_reset_sent": "La demande de réinitialisation du mot de passe a bien été envoyée", "invalid_email": "Email invalide / L'email n'existe pas !", - "password_too_short": "Le mot de passe est trop court, veuillez entre un mot de passe différent.", - "passwords_do_not_match": "Les deux mots de passe saisient ne correspondent pas." + "password_too_short": "Le mot de passe est trop court, veuillez entrer un mot de passe différent.", + "passwords_do_not_match": "Les deux mots de passe saisis ne correspondent pas.", + "password_expired": "Votre mot de passe a expiré, veuillez choisir un nouveau mot de passe." } \ No newline at end of file diff --git a/public/language/fr/search.json b/public/language/fr/search.json index f05eec953d..cfbabc6dd0 100644 --- a/public/language/fr/search.json +++ b/public/language/fr/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 résultat(s) correspondant(s) à \"%2\", (%3 secondes)", "no-matches": "Aucune réponse trouvée", + "advanced-search": "Recherche avancée", "in": "Dans", - "by": "Par", "titles": "Titres", "titles-posts": "Titres et Messages", "posted-by": "Posté par", diff --git a/public/language/fr/tags.json b/public/language/fr/tags.json index 477f1a0b56..14881863d7 100644 --- a/public/language/fr/tags.json +++ b/public/language/fr/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Il n'y a aucun sujet ayant ce mot-clé", "tags": "Mots-clés", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Entrez les mots-clés ici. Chaque mot doit faire entre %1 et %2 caractères.", "enter_tags_here_short": "Entrez des mots-clés...", "no_tags": "Il n'y a pas encore de mots-clés." } \ No newline at end of file diff --git a/public/language/fr/user.json b/public/language/fr/user.json index 2f60041e7e..b2e6c5a902 100644 --- a/public/language/fr/user.json +++ b/public/language/fr/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "S'abonner", "unfollow": "Se désabonner", + "more": "Plus", "profile_update_success": "Le profil a bien été mis à jour !", "change_picture": "Changer d'image", "edit": "Éditer", @@ -47,7 +48,6 @@ "upload_picture": "Envoyer l'image", "upload_a_picture": "Envoyer une image", "image_spec": "Vous ne pouvez envoyer que des fichiers PNG, JPG ou GIF", - "max": "max.", "settings": "Paramètres", "show_email": "Afficher mon email", "show_fullname": "Afficher mon nom complet", @@ -60,7 +60,7 @@ "digest_monthly": "Mensuel", "send_chat_notifications": "Envoyer un e-mail si un nouveau message de chat arrive lorsque je ne suis pas en ligne", "send_post_notifications": "Envoyer un email lors de réponses envoyées aux sujets auxquels je suis abonné.", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Certains réglages nécessitent un rechargement. Cliquez ici pour recharger la page.", "has_no_follower": "Cet utilisateur n'est suivi par personne :(", "follows_no_one": "Cet utilisateur ne suit personne :(", "has_no_posts": "Ce membre n'a rien posté pour le moment", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Cet utilisateur ne suis encore aucun sujet.", "email_hidden": "Email masqué", "hidden": "masqué", - "paginate_description": "Utiliser la pagination des sujets et des messages au lieu du défilement infini.", + "paginate_description": "Utiliser la pagination des sujets et des messages à la place du défilement infini.", "topics_per_page": "Sujets par page", "posts_per_page": "Messages par page", "notification_sounds": "Émettre un son lors de la réception de notifications.", "browsing": "Paramètres de navigation", - "open_links_in_new_tab": "Ouvrir les liens externes dans un nouvel onglet ?", + "open_links_in_new_tab": "Ouvrir les liens externes dans un nouvel onglet", "enable_topic_searching": "Activer la recherche dans les sujets", - "topic_search_help": "Une fois activé, la recherche dans les sujets va remplacer la recherche de page du navigateur et vous permettra de rechercher dans l'intégralité d'un sujet au lieu des seuls posts chargés.", - "follow_topics_you_reply_to": "Suivre les sujets auxquels vous répondez.", - "follow_topics_you_create": "Suivre les sujets que vous créez.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "Une fois activé, la recherche dans les sujets va remplacer la recherche de page du navigateur et vous permettra de rechercher dans l'intégralité d'un sujet au lieu des seuls posts affichés à l'écran.", + "follow_topics_you_reply_to": "Suivre les sujets auxquels vous répondez", + "follow_topics_you_create": "Suivre les sujets que vous créez", + "grouptitle": "Sélectionnez le titre de groupe que vous souhaitez afficher", + "no-group-title": "Aucun titre de groupe" } \ No newline at end of file diff --git a/public/language/he/category.json b/public/language/he/category.json index 66e87dd4e3..03fb624861 100644 --- a/public/language/he/category.json +++ b/public/language/he/category.json @@ -5,5 +5,8 @@ "browsing": "צופים בנושא זה כעת", "no_replies": "אין תגובות", "share_this_category": "שתף קטגוריה זו", - "ignore": "התעלם" + "watch": "Watch", + "ignore": "התעלם", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/he/error.json b/public/language/he/error.json index e210d21334..d3171986cc 100644 --- a/public/language/he/error.json +++ b/public/language/he/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "שם משתמש קצר מדי", "username-too-long": "שם משתמש ארוך מדי", "user-banned": "המשתמש חסום", - "user-too-new": "מצטערים, אתה צריך לחכות %1 שניות לפני שתוכל לפרסם את הפוסט הראשון שלך", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "קטגוריה אינה קיימת", "no-topic": "נושא אינו קיים", "no-post": "פוסט אינו קיים", @@ -35,17 +36,17 @@ "no-emailers-configured": "לא נמצאו פלאגינים למייל, לכן אין אפשרות לשלוח מייל ניסיון.", "category-disabled": "קטגוריה לא פעילה", "topic-locked": "נושא נעול", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "אנא המתן לסיום ההעלאות", - "content-too-short": "יש להכניס פוסט ארוך יותר. פוסטים חייבים להכיל לפחות %1 תווים.", - "content-too-long": "יש לקצר את הפוסט. פוסטים אינם יכולים להיות ארוכים מ %1 תווים.", - "title-too-short": "יש להכניס כותרת ארוכה יותר. כותרות חייבות להכיל לפחות %1 תווים.", - "title-too-long": "יש לקצר את הכותרת. כותרות אינן יכולות להיות ארוכות מ %1 תווים.", - "too-many-posts": "אתה יכול להעלות פוסט כל %1 שניות בלבד - אנא המתן.", - "too-many-posts-newbie": "כמשתמש חדש, אתה יכול להעלות פוסט כל %1 שניות עד שצברת %2 מוניטין - אנא המתן.", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "גודל קובץ מקסימלי הינו %1 קילובייט - אנא העלה קובץ קטן יותר.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "לא ניתן להצביע לפוסט שלך", "already-favourited": "כבר הוספת פוסט זה למועדפים", "already-unfavourited": "כבר הסרת פוסט זה מהמועדפים", @@ -62,10 +63,11 @@ "post-already-restored": "פוסט זה כבר שוחזר", "topic-already-deleted": "נושא זה כבר נמחק", "topic-already-restored": "נושא זה כבר שוחזר", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "תמונות ממוזערות לנושא אינן מאופשרות.", "invalid-file": "קובץ לא תקין", "uploads-are-disabled": "העלאת קבצים אינה מאופשרת", - "signature-too-long": "מצטערים, אורך החתימה המקסימלי הוא %1 תווים.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "לא ניתן לעשות צ'אט עם עצמך!", "chat-restricted": "משתמש זה חסם את הודעות הצ'אט שלו ממשתמשים זרים. המשתמש חייב לעקוב אחריך לפני שתוכל לשוחח איתו בצ'אט", "too-many-messages": "שלחת יותר מדי הודעות, אנא המתן לזמן מה.", diff --git a/public/language/he/groups.json b/public/language/he/groups.json index a6bcffab82..bb4e535bb8 100644 --- a/public/language/he/groups.json +++ b/public/language/he/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "ניהול הקבוצה", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/he/modules.json b/public/language/he/modules.json index f4fe8cd0ef..d0661a98fa 100644 --- a/public/language/he/modules.json +++ b/public/language/he/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 ימים", "chat.thirty_days": "30 ימים", "chat.three_months": "3 חודשים", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 אמר ב%2:", "composer.user_said": "%1 אמר:", "composer.discard": "האם למחוק פוסט זה?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/he/pages.json b/public/language/he/pages.json index 8a39e4fa58..892ba1a918 100644 --- a/public/language/he/pages.json +++ b/public/language/he/pages.json @@ -5,7 +5,8 @@ "recent": "נושאים אחרונים", "users": "משתמשים רשומים", "notifications": "התראות", - "tags": "נושאים שתויגו תחת \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "עורך את %1", "user.following": "אנשים ש%1 עוקב אחריהם", "user.followers": "אנשים שעוקבים אחרי %1", diff --git a/public/language/he/reset_password.json b/public/language/he/reset_password.json index 2ed1435b18..854ae07ffe 100644 --- a/public/language/he/reset_password.json +++ b/public/language/he/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "קוד איפוס סיסמה נשלח", "invalid_email": "מייל שגוי / כתובת מייל לא נמצאה", "password_too_short": "הסיסמה שבחרת קצרה מדי, אנא בחר סיסמה שונה.", - "passwords_do_not_match": "הסיסמאות שהזנת אינן תואמות." + "passwords_do_not_match": "הסיסמאות שהזנת אינן תואמות.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/he/search.json b/public/language/he/search.json index b9ca111d91..5de6fe4b86 100644 --- a/public/language/he/search.json +++ b/public/language/he/search.json @@ -1,8 +1,8 @@ { "results_matching": "נמצאו %1 תוצאות עבור החיפוש \"%2\", (%3 שניות)", "no-matches": "לא נמצאו תוצאות", + "advanced-search": "Advanced Search", "in": "ב", - "by": "על ידי", "titles": "כותרות", "titles-posts": "כותרות ופוסטים", "posted-by": "פורסם על-ידי", diff --git a/public/language/he/user.json b/public/language/he/user.json index 61a45f3c5c..c2989b6233 100644 --- a/public/language/he/user.json +++ b/public/language/he/user.json @@ -27,6 +27,7 @@ "chat": "צ'אט", "follow": "עקוב", "unfollow": "הפסק לעקוב", + "more": "More", "profile_update_success": "הפרופיל עודכן בהצלחה!", "change_picture": "שנה תמונה", "edit": "ערוך", @@ -47,7 +48,6 @@ "upload_picture": "העלה תמונה", "upload_a_picture": "העלה תמונה", "image_spec": "ניתן להעלות תמונות בפורמט PNG, JPG או GIF בלבד", - "max": "מקסימום", "settings": "הגדרות", "show_email": "פרסם את כתובת האימייל שלי", "show_fullname": "הצג את שמי המלא", @@ -68,15 +68,16 @@ "has_no_watched_topics": "משתמש זה לא עקב אחרי אף נושא עדיין.", "email_hidden": "כתובת אימייל מוסתרת", "hidden": "מוסתר", - "paginate_description": "צור עימוד לנושאים במקום לטעון את כל התוכן בעמוד אחד.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "כמות נושאים בעמוד", "posts_per_page": "כמות פוסטים בעמוד", - "notification_sounds": "השמע צליל כאשר מתקבלת הודעה עבורך.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "הגדרות צפייה", - "open_links_in_new_tab": "פתח לינקים חיצוניים בטאב חדש?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "הפעל חיפוש בתוך נושא", - "topic_search_help": "אם מופעל, חיפוש בתוך נושא יעקוף את מנגנון החיפוש הרגיל של הדפדפן שלך על מנת לאפשר לך לחפש בתוך כל הנושא ולא רק מה שמוצג כרגע בעמוד.", - "follow_topics_you_reply_to": "עקוב אחר נושאים שהגבת עליהם.", - "follow_topics_you_create": "עקוב אחר נושאים שיצרת.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/hu/category.json b/public/language/hu/category.json index 2815eaab43..e77bbe78a5 100644 --- a/public/language/hu/category.json +++ b/public/language/hu/category.json @@ -5,5 +5,8 @@ "browsing": "böngészés", "no_replies": "Nem érkezett válasz", "share_this_category": "Kategória megosztása", - "ignore": "Ignorálás" + "watch": "Watch", + "ignore": "Ignorálás", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/hu/error.json b/public/language/hu/error.json index 39ec76c011..e84486eb06 100644 --- a/public/language/hu/error.json +++ b/public/language/hu/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "Ez a fórum e-mail megerősítést kíván, kérlek kattints ide egy cím beírásához", "email-confirm-failed": "Nem tudtuk ellenőrizni az e-mail címedet, kérlek próbálkozz később.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Túl rövid felhasználónév", "username-too-long": "Túl hosszú felhasználónév", "user-banned": "Kitiltott felhasználó", - "user-too-new": "Sajnáljuk, várnod kell %1 másodpercet, mielőtt beküldenéd az első hozzászólásodat", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Nem létező kategória", "no-topic": "Nem létező téma", "no-post": "Nem létező hozzászólás", @@ -35,17 +36,17 @@ "no-emailers-configured": "Nincs levelező beállítva, ezért a teszt e-mail nem került kiküldésre.", "category-disabled": "Kategória kikapcsolva", "topic-locked": "Téma lezárva", - "post-edit-duration-expired": "A hozzászólásaidat %1 másodpercig szerkesztheted, miután beküldted azt", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Kérlek várj, amíg a feltöltés befejeződik.", - "content-too-short": "Kérlek hosszabb hozzászólást írj be. Legalább %1 karakternek kell lennie.", - "content-too-long": "Kérlek rövidebb hozzászólást írj be. Legfeljebb %1 karakter lehet.", - "title-too-short": "Kérlek hosszabb címet válassz. Legalább %1 karakternek kell lennie.", - "title-too-long": "Kérlek rövidebb címet válassz. Legfeljebb %1 karakter lehet.", - "too-many-posts": "%1 másodpercenként csak egy hozzászólást írhatsz - kérlek várj mielőtt újból hozzászólnál", - "too-many-posts-newbie": "Mint friss tag, csak egy hozzászólást küldhetsz be %1 másodpercenként, míg nem kapsz %2 jó hírnevet - kérlek várj mielőtt újból hozzászólnál", - "tag-too-short": "Kérlek hosszabb címkét válassz. Legalább %1 karakter hosszúnak kell lennie", - "tag-too-long": "Kérlek rövidebb címkét válassz. Legfeljebb %1 karakter lehet", - "file-too-big": "Maximális engedélyezett fájlméret %1 kbs - kérlek kisebb fájlt tölts fel", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Nem szavazhatsz a saját hozzászólásodra", "already-favourited": "Már bejelölted Kedvencnek ezt a hozzászólást", "already-unfavourited": "Már kivetted a Kedvenceid közül ezt a hozzászólást", @@ -62,10 +63,11 @@ "post-already-restored": "Ez a bejegyzés már visszaállításra került", "topic-already-deleted": "Ezt a témakör már törlésre került", "topic-already-restored": "Ez a témakör már helyreállításra került", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Témakör bélyegképek tíltásra kerültek.", "invalid-file": "Érvénytelen fájl", "uploads-are-disabled": "A feltöltés nem engedélyezett", - "signature-too-long": "Sajnáljuk, az aláírás nem lehet hosszabb %1 karakternél.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nem cseveghetsz magaddal!", "chat-restricted": "Ez a felhasználó korlátozta a chat beállításait. Csak akkor cseveghetsz vele, miután felvett a követettek közé téged", "too-many-messages": "Túl sok üzenetet küldtél, kérlek várj egy picit.", diff --git a/public/language/hu/groups.json b/public/language/hu/groups.json index 75a7b053d5..4fff97f77a 100644 --- a/public/language/hu/groups.json +++ b/public/language/hu/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/hu/modules.json b/public/language/hu/modules.json index c708c09d98..6c1d50915a 100644 --- a/public/language/hu/modules.json +++ b/public/language/hu/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 nap", "chat.thirty_days": "30 nap", "chat.three_months": "3 hónap", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 válasza erre %2:", "composer.user_said": "%1 válasza:", "composer.discard": "Biztosan elvetjük ezt a hozzászólást?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/hu/pages.json b/public/language/hu/pages.json index 547e924217..e628721286 100644 --- a/public/language/hu/pages.json +++ b/public/language/hu/pages.json @@ -5,7 +5,8 @@ "recent": "Friss Topikok", "users": "Regisztrált Felhasználók", "notifications": "Értesítések", - "tags": "Témák feliratú alatt \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Szerkesztés \"%1\"", "user.following": "Tagok akiket %1 követ", "user.followers": "Tagok akik követik %1 -t", diff --git a/public/language/hu/reset_password.json b/public/language/hu/reset_password.json index 3f5721d6d6..e7183f81cc 100644 --- a/public/language/hu/reset_password.json +++ b/public/language/hu/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Jelszó-visszaállítás elküldve", "invalid_email": "Helytelen E-mail cím / Nem létező E-mail cím!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/hu/search.json b/public/language/hu/search.json index 5c09b69e77..d3df716f81 100644 --- a/public/language/hu/search.json +++ b/public/language/hu/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 eredmény(ek) erre \"%2\" (%3 másodperc alatt)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/hu/user.json b/public/language/hu/user.json index ba7f403c7c..f35879c80e 100644 --- a/public/language/hu/user.json +++ b/public/language/hu/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Követés", "unfollow": "Nem követem", + "more": "More", "profile_update_success": "Profil sikeresen frissítve!", "change_picture": "Kép megváltoztatása", "edit": "Szerkeszt", @@ -47,7 +48,6 @@ "upload_picture": "Kép feltöltése", "upload_a_picture": "Egy kép feltöltése", "image_spec": "Csak PNG, JPG vagy GIF kiterjesztésű fájlokat tölthetsz fel", - "max": "max.", "settings": "Beállítások", "show_email": "E-mail címem mutatása", "show_fullname": "A teljes nevem mutatása", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Ez a felhasználó nem figyel egyetlen témát sem még.", "email_hidden": "E-mail rejtett", "hidden": "rejtett", - "paginate_description": "Oldalszámok használata a témáknál és hozzászólásoknál a végtelen görgetés helyett.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Téma oldalanként", "posts_per_page": "Hozzászólás oldalanként", - "notification_sounds": "Hang lejátszása ha értesítés érkezett.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Kívülre mutató linkek megnyitása új fülön?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Témán belüli keresés bekapcsolása", - "topic_search_help": "Amennyiben be van kapcsolva, a témán belüli keresés felül fogja írni a böngésző alapértelmezett oldalon belüli keresőjét és engedélyezni fogja neked, hogy a teljes témában kereshess, ne csak abban, ami jelenleg is megjelenik a képernyőn.", - "follow_topics_you_reply_to": "Minden olyan téma követése, amihez hozzászóltál.", - "follow_topics_you_create": "Minden általad létrehozott téma követése.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/id/category.json b/public/language/id/category.json index 7b0ff0f6eb..46613ff1ad 100644 --- a/public/language/id/category.json +++ b/public/language/id/category.json @@ -5,5 +5,8 @@ "browsing": "penjelajahan", "no_replies": "Belum ada orang yang menjawab", "share_this_category": "Bagikan kategori ini", - "ignore": "Abaikan" + "watch": "Watch", + "ignore": "Abaikan", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/id/error.json b/public/language/id/error.json index 6f1e7b289f..508ebfcc6b 100644 --- a/public/language/id/error.json +++ b/public/language/id/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username terlalu pendek", "username-too-long": "Username terlalu panjang", "user-banned": "Pengguna dibanned", - "user-too-new": "Maaf kamu harus menunggu %1 detik sebelum membuat post pertama", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategori tidak ditemukan", "no-topic": "Topik tidak ditemukan", "no-post": "Post tidak ditemukan", @@ -35,17 +36,17 @@ "no-emailers-configured": "Tidak ada plugin email, jadi test email tidak dapat dikirim", "category-disabled": "Kategori ditiadakan", "topic-locked": "Topik dikunci", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Tunggu proses upload sampai selesai", - "content-too-short": "Mohon masukkan posting yang lebih panjang. Posting harus memuat setidaknya %1 karakter.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Mohon masukkan judul yang lebih panjang. Judul harus memuat setidaknya %1 karakter.", - "title-too-long": "Mohon masukkan judul yang lebih pendek. Judul tidak dapat melebihi %1 karakter.", - "too-many-posts": "Kamu hanya dapat melakukan posting satu kali setiap %1 detik - mohon tunggu beberapa saat sebelum melakukan posting kembali", - "too-many-posts-newbie": "Sebagai pengguna baru, kamu hanya diijinkan membuat posting satu kali setiap %1 detik sampai kamu mendapatkan %2 reputasi - mohon tunggu beberapa saat sebelum melakukan posting kembali", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Ukuran file yang boleh diupload %1 kbs - mohon upload file dengan ukuran lebih kecil", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Kamu tidak dapat vote postingan sendiri", "already-favourited": "Post ini sudah kamu favorit", "already-unfavourited": "Postingan ini sudah kamu unfavorit", @@ -62,10 +63,11 @@ "post-already-restored": "Postingan ini sudah direstore", "topic-already-deleted": "Topik ini sudah dihapus", "topic-already-restored": "Topik ini sudah direstore", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Thumbnail di topik ditiadakan", "invalid-file": "File Salah", "uploads-are-disabled": "Upload ditiadakan", - "signature-too-long": "Maaf, tanda pengenalmu tidak dapat melebihi %1 karakter.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Kamu tidak dapat chat dengan akun sendiri", "chat-restricted": "Pengguna ini telah membatasi percakapa mereka. Mereka harus mengikutimu sebelum kamu dapat melakukan percakapan dengan mereka ", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/id/groups.json b/public/language/id/groups.json index f70bea0bf5..8cf87045b7 100644 --- a/public/language/id/groups.json +++ b/public/language/id/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/id/modules.json b/public/language/id/modules.json index 5bd4fe85c6..9c412d98fe 100644 --- a/public/language/id/modules.json +++ b/public/language/id/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Hari", "chat.thirty_days": "30 Hari", "chat.three_months": "3 Bulan", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 berkata di %2: ", "composer.user_said": "%1 berkata:", "composer.discard": "Kamu yakin akan membuang posting ini?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/id/pages.json b/public/language/id/pages.json index 08872d82de..43e2e94e40 100644 --- a/public/language/id/pages.json +++ b/public/language/id/pages.json @@ -5,7 +5,8 @@ "recent": "Topik Terkini", "users": "Pengguna Terdaftar", "notifications": "Pemberitahuan", - "tags": "Topik ditag dalam \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Sedang merubah \"%1\"", "user.following": "Mengikuti Pengguna %1", "user.followers": "Pengguna yang mengikuti %1", diff --git a/public/language/id/reset_password.json b/public/language/id/reset_password.json index ec65c5ca68..ddbcd319fe 100644 --- a/public/language/id/reset_password.json +++ b/public/language/id/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Pengaturan Kembali Kata Sandi telah DIkirim", "invalid_email": "Email Salah / Email tidak ada!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/id/search.json b/public/language/id/search.json index 544579a3ff..351a44e4e5 100644 --- a/public/language/id/search.json +++ b/public/language/id/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 hasil yang sesuai dengan \"%2\", (%3 detik)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/id/user.json b/public/language/id/user.json index e7a7112465..5faa4375a1 100644 --- a/public/language/id/user.json +++ b/public/language/id/user.json @@ -27,6 +27,7 @@ "chat": "Percakapan", "follow": "Ikuti", "unfollow": "Tinggalkan", + "more": "More", "profile_update_success": "Profil berhasil diperbarui!", "change_picture": "Ganti Gambar/Foto", "edit": "Perbarui", @@ -47,7 +48,6 @@ "upload_picture": "Unggah gambar/foto", "upload_a_picture": "Unggah sebuah gambar/foto", "image_spec": "Kamu hanya diijinkan mengunggah berkas dengan format PNG, JPG, atau GIF", - "max": "maks.", "settings": "Pengaturan", "show_email": "Tampilkan Email Saya", "show_fullname": "Tampilkan Nama Lengkap Saya", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Email Disembunyikan", "hidden": "disembunyikan", - "paginate_description": "Gunakan halaman topik dan posting sebagai ganti penggunaan gulungan tanpa batas.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Topik per Halaman", "posts_per_page": "Posting per Halaman", - "notification_sounds": "Mainkan suara saat kamu menerima pemberitahuan.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Pengaturan Penelusuran", - "open_links_in_new_tab": "Buka tautan ke luar pada tab baru?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Gunakan Pencarian Di dalam Topik", - "topic_search_help": "Jika digunakan, pencarian di dalam topik akan mengambil alih tindakan pencarian default browser dan mengijinkan kamu untuk mencari keseluruhan topik sebagai pengganti pencarian hanya yang tampil pada layar saja.", - "follow_topics_you_reply_to": "Ikuti topik yang kamu balas.", - "follow_topics_you_create": "Ikuti topik yang kamu buat.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/it/category.json b/public/language/it/category.json index 5721f6eb3f..224bd2b833 100644 --- a/public/language/it/category.json +++ b/public/language/it/category.json @@ -5,5 +5,8 @@ "browsing": "visualizzando", "no_replies": "Nessuno ha risposto", "share_this_category": "Condividi questa Categoria", - "ignore": "Ignora" + "watch": "Osserva", + "ignore": "Ignora", + "watch.message": "Non stai seguendo gli aggiornamenti di questa categoria", + "ignore.message": "Da ora saranno ignorati gli aggiornamenti di questa categoria" } \ No newline at end of file diff --git a/public/language/it/error.json b/public/language/it/error.json index 6858916193..fc59c81e2c 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -18,13 +18,14 @@ "username-taken": "Nome utente già preso", "email-taken": "Email già esistente", "email-not-confirmed": "La tua Email deve essere ancora confermata, per favore clicca qui per confermare la tua Email.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "Non potrai chattare finchè non avrai confermato la tua email, per favore clicca qui per farlo ora.", "no-email-to-confirm": "Questo forum richiede la conferma dell'indirizzo email, per favore clicca qui per inserirne uno", "email-confirm-failed": "Non possiamo confermare la tua email, per favore prova ancora più tardi.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Nome utente troppo corto", "username-too-long": "Nome utente troppo lungo", "user-banned": "Utente bannato", - "user-too-new": "Spiacenti, devi aspettare %1 secondi prima di creare il tuo primo Post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "La Categoria non esiste", "no-topic": "Il Topic non esiste", "no-post": "Il Post non esiste", @@ -35,24 +36,24 @@ "no-emailers-configured": "Nessun plugin per le email è caricato, quindi la mail di test non può essere inviata", "category-disabled": "Categoria disabilitata", "topic-locked": "Discussione Bloccata", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Per favore attendere il completamento degli uploads.", - "content-too-short": "Inserisci un post più lungo. Il Messaggio deve contenere almeno %1 caratteri.", - "content-too-long": "Per favore inserisci un post più breve. I post non possono essere più lunghi di %1 caratteri.", - "title-too-short": "Inserisci un titolo più lungo. I titoli devono contenere almeno %1 caratteri.", - "title-too-long": "Per favore inserire un titolo più corto, non può essere più lungo di %1 caratteri.", - "too-many-posts": "È possibile inserire un Post ogni %1 secondi - si prega di attendere prima di postare di nuovo", - "too-many-posts-newbie": "Come nuovo utente puoi postare solamente una volta ogni %1 secondi finché non hai raggiunto un livello di reputazione %2 - per favore attendi prima di inviare ancora", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "La dimensione massima del file consentita è di % 1 kbs - si prega di caricare un file più piccolo", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Non puoi votare il tuo stesso post", "already-favourited": "Hai già inserito tra i preferiti questo post", "already-unfavourited": "Hai già inserito tra i Non Preferiti questo post", "cant-ban-other-admins": "Non puoi bannare altri amministratori!", "invalid-image-type": "Tipo dell'immagine non valido. I tipi permessi sono: %1", "invalid-image-extension": "Estensione immagine non valida", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "invalid-file-type": "Tipo di file non valido. I formati consentiti sono: %1", "group-name-too-short": "Nome del Gruppo troppo corto", "group-already-exists": "Il Gruppo esiste già", "group-name-change-not-allowed": "Il cambio di nome al Gruppo non è consentito", @@ -62,10 +63,11 @@ "post-already-restored": "Questo Post è già stato ripristinato", "topic-already-deleted": "Questo Topic è già stato cancellato", "topic-already-restored": "Questo Topic è già stato ripristinato", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Le anteprime della Discussione sono disabilitate.", "invalid-file": "File non valido", "uploads-are-disabled": "Uploads disabilitati", - "signature-too-long": "Spiacenti, la tua firma non può essere lunga %1 caratteri.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Non puoi chattare con te stesso!", "chat-restricted": "Questo utente ha ristretto i suoi messaggi in chat alle persone che segue. Per poter chattare con te ti deve prima seguire.", "too-many-messages": "Hai inviato troppi messaggi, aspetta un attimo.", @@ -76,6 +78,6 @@ "reload-failed": "NodeBB ha incontrato un problema durante il ricaricamento: \"%1\". NodeBB continuerà a servire gli assets esistenti lato client, così puoi annullare quello che hai fatto prima di ricaricare.", "registration-error": "Errore nella registrazione", "parse-error": "Qualcosa è andato storto durante l'analisi della risposta proveniente dal server", - "wrong-login-type-email": "Please use your email to login", + "wrong-login-type-email": "Per favore usa la tua email per accedere", "wrong-login-type-username": "Per favore usa il tuo nome utente per accedere" } \ No newline at end of file diff --git a/public/language/it/groups.json b/public/language/it/groups.json index 96b915d56e..4b96975fac 100644 --- a/public/language/it/groups.json +++ b/public/language/it/groups.json @@ -4,8 +4,8 @@ "owner": "Proprietario del Gruppo", "new_group": "Crea Nuovo Gruppo", "no_groups_found": "Non ci sono gruppi da vedere", - "pending.accept": "Accept", - "pending.reject": "Reject", + "pending.accept": "Accetta", + "pending.reject": "Rigetta", "cover-instructions": "Drag and Drop una fotografia, spostarla ad una posizione, e premere Salva", "cover-change": "Cambia", "cover-save": "Salva", @@ -15,20 +15,22 @@ "details.pending": "Membri in attesa", "details.has_no_posts": "I membri di questo gruppo non hanno ancora postato.", "details.latest_posts": "Ultimi Post", - "details.private": "Private", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", + "details.private": "Privato", + "details.grant": "Concedi / Rimuovi la Proprietà", + "details.kick": "Espelli", "details.owner_options": "Amministratore del Grupo", - "details.group_name": "Group Name", - "details.description": "Description", - "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", - "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", - "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", - "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", + "details.group_name": "Nome Gruppo", + "details.member_count": "Totale Membri", + "details.creation_date": "Data di Creazione", + "details.description": "Descrizione", + "details.badge_preview": "Anteprima del Badge", + "details.change_icon": "Cambia Icona", + "details.change_colour": "Cambia Colore", + "details.badge_text": "Testo del Badge", + "details.userTitleEnabled": "Visualizza il Badge", + "details.private_help": "Se abilitato, l'ingresso ai gruppi richiede l'approvazione di uno dei proprietari", + "details.hidden": "Nascosto", + "details.hidden_help": "Se abilitato, questo gruppo non sarà visibile nella lista dei gruppi e gli utenti dovranno essere invitati manualmente", "event.updated": "I dettagli del Gruppo sono stati aggiornati", "event.deleted": "Il gruppo \"%1\" è stato eliminato" } \ No newline at end of file diff --git a/public/language/it/login.json b/public/language/it/login.json index 75747e30d1..f04d6cdfb4 100644 --- a/public/language/it/login.json +++ b/public/language/it/login.json @@ -1,6 +1,6 @@ { - "username-email": "Username / Email", - "username": "Username", + "username-email": "Nome utente / Email", + "username": "Nome utente", "email": "Email", "remember_me": "Ricordami?", "forgot_password": "Password dimenticata?", diff --git a/public/language/it/modules.json b/public/language/it/modules.json index dc6d3dcf78..4b9c4905c1 100644 --- a/public/language/it/modules.json +++ b/public/language/it/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Giorni", "chat.thirty_days": "30 Giorni", "chat.three_months": "3 Mesi", + "composer.compose": "Componi", + "composer.show_preview": "Visualizza Anteprima", + "composer.hide_preview": "Nascondi Anteprima", "composer.user_said_in": "%1 ha detto in %2:", "composer.user_said": "%1 ha detto:", "composer.discard": "Sei sicuro di voler scartare questo post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Invia e Blocca", + "composer.toggle_dropdown": "Mostra/Nascondi menu a discesa" } \ No newline at end of file diff --git a/public/language/it/pages.json b/public/language/it/pages.json index f7395c9d50..31c72ce45b 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -5,7 +5,8 @@ "recent": "Discussioni Recenti", "users": "Utenti Registrati", "notifications": "Notifiche", - "tags": "Discussioni taggate \"%1\"", + "tags": "Tags", + "tag": "Discussioni taggate \"%1\"", "user.edit": "Modificando \"%1\"", "user.following": "Persone seguite da %1", "user.followers": "Persone che seguono %1", @@ -14,7 +15,7 @@ "user.groups": "%1's Gruppi", "user.favourites": "Post Favoriti da %1", "user.settings": "Impostazioni Utente", - "user.watched": "Topics watched by %1", + "user.watched": "Argomenti osservati da %1", "maintenance.text": "%1 è attualmente in manutenzione. Per favore ritorna più tardi.", "maintenance.messageIntro": "Inoltre, l'amministratore ha lasciato questo messaggio:" } \ No newline at end of file diff --git a/public/language/it/recent.json b/public/language/it/recent.json index bf4485644c..c61e51f150 100644 --- a/public/language/it/recent.json +++ b/public/language/it/recent.json @@ -6,7 +6,7 @@ "year": "Anno", "alltime": "Sempre", "no_recent_topics": "Non ci sono discussioni recenti.", - "no_popular_topics": "There are no popular topics.", + "no_popular_topics": "Non ci sono argomenti popolari.", "there-is-a-new-topic": "C'è un nuovo topic.", "there-is-a-new-topic-and-a-new-post": "C'è un nuovo topic e un nuovo post.", "there-is-a-new-topic-and-new-posts": "C'è una nuova discussione e %1 nuovi post.", diff --git a/public/language/it/reset_password.json b/public/language/it/reset_password.json index d89642e43b..f324a0854d 100644 --- a/public/language/it/reset_password.json +++ b/public/language/it/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Password Reset Inviata", "invalid_email": "Email invalida / L'email non esiste!", "password_too_short": "La password inserita è troppo corta, inserisci una password differente.", - "passwords_do_not_match": "Le due password che hai inserito non corrispondono." + "passwords_do_not_match": "Le due password che hai inserito non corrispondono.", + "password_expired": "La tua password è scaduta, per favore scegline una nuova" } \ No newline at end of file diff --git a/public/language/it/search.json b/public/language/it/search.json index 14ddeaa98a..a3e73da887 100644 --- a/public/language/it/search.json +++ b/public/language/it/search.json @@ -1,13 +1,13 @@ { "results_matching": "%1 risultato(i) corrispondente(i) \"%2\", (%3 secondi)", "no-matches": "Nessuna corrispondenza trovata", + "advanced-search": "Ricerca Avanzata", "in": "In", - "by": "Da", "titles": "Titoli", "titles-posts": "Titoli e Messaggi", "posted-by": "Pubblicato da", "in-categories": "In Categorie", - "search-child-categories": "Search child categories", + "search-child-categories": "Cerca nelle sottocategorie", "reply-count": "Numero Risposte", "at-least": "Almeno", "at-most": "Al massimo", @@ -23,7 +23,7 @@ "six-months": "Sei mesi", "one-year": "Un anno", "sort-by": "Ordina per", - "last-reply-time": "Last reply time", + "last-reply-time": "Ora dell'ultima risposta", "topic-title": "Titolo argomento", "number-of-replies": "Numero di risposte", "number-of-views": "Numero di visite", @@ -32,9 +32,9 @@ "category": "Categoria", "descending": "In ordine decrescente", "ascending": "In ordine crescente", - "save-preferences": "Save preferences", - "clear-preferences": "Clear preferences", - "search-preferences-saved": "Search preferences saved", - "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "save-preferences": "Salva preferenze", + "clear-preferences": "Pulisci preferenze", + "search-preferences-saved": "Cerca nelle preferenze salvate", + "search-preferences-cleared": "Cerca nelle preferenze pulite", + "show-results-as": "Visualizza risultati come" } \ No newline at end of file diff --git a/public/language/it/tags.json b/public/language/it/tags.json index 5fcdf9ff47..9b2f9735e4 100644 --- a/public/language/it/tags.json +++ b/public/language/it/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Non ci sono discussioni con questo tag.", "tags": "Tags", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Inserisci qui i tag, tra %1 e %2 caratteri ognuno.", "enter_tags_here_short": "Inserisci i tags...", "no_tags": "Non ci sono ancora tags." } \ No newline at end of file diff --git a/public/language/it/user.json b/public/language/it/user.json index 3dd7971b11..ec6779bccc 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Segui", "unfollow": "Smetti di seguire", + "more": "Altro", "profile_update_success": "Profilo aggiornato correttamente!", "change_picture": "Cambia Foto", "edit": "Modifica", @@ -47,7 +48,6 @@ "upload_picture": "Carica foto", "upload_a_picture": "Carica una foto", "image_spec": "Puoi caricare solo file PNG, JPG o GIF", - "max": "massimo.", "settings": "Impostazioni", "show_email": "Mostra la mia Email", "show_fullname": "Vedi il Mio Nome Completo", @@ -60,7 +60,7 @@ "digest_monthly": "Mensile", "send_chat_notifications": "Invia una email se arriva un nuovo messaggio di chat e non sono online", "send_post_notifications": "Invia una email quando le risposte sono fatte a discussioni a cui sono sottoscritto", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Alcuni cambiamenti di impostazioni richiedono un ricaricamento. Clicca qui per ricaricare la pagina.", "has_no_follower": "Questo utente non è seguito da nessuno :(", "follows_no_one": "Questo utente non segue nessuno :(", "has_no_posts": "Questo utente non ha ancora postato nulla.", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Questo utente non sta ancora osservando alcuna discussione.", "email_hidden": "Email Nascosta", "hidden": "nascosta", - "paginate_description": "Dividi discussioni e post in pagine anziché usare lo scroll infinito.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Discussioni per Pagina", "posts_per_page": "Post per Pagina", - "notification_sounds": "Riproduci un suono quando ricevi una notifica.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Impostazioni di Navigazione", - "open_links_in_new_tab": "Aprire i link esterni in una nuova tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Abilita la ricerca negli argomenti", - "topic_search_help": "Se abilitata, la ricerca negli argomenti sovrascriverà la pagina di ricerca preimpostata del browser per consentirti di cercare all'interno delle discussioni, anziché soltanto nel contenuto visibile a schermo.", - "follow_topics_you_reply_to": "Segui le discussioni in cui rispondi.", - "follow_topics_you_create": "Segui le discussioni che crei.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Seleziona il titolo del gruppo che vorresti vedere", + "no-group-title": "Nessun titolo al gruppo" } \ No newline at end of file diff --git a/public/language/ja/category.json b/public/language/ja/category.json index 6fbd386dac..8d792897c4 100644 --- a/public/language/ja/category.json +++ b/public/language/ja/category.json @@ -5,5 +5,8 @@ "browsing": "閲覧中", "no_replies": "返事はまだありません", "share_this_category": "この板を共有", - "ignore": "Ignore" + "watch": "Watch", + "ignore": "Ignore", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/ja/error.json b/public/language/ja/error.json index d53e73b76e..f4edefb38f 100644 --- a/public/language/ja/error.json +++ b/public/language/ja/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "ユーザーが停止された", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "この板は無効された", "topic-locked": "スレッドがロックされた", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "アップロードが完成するまでお待ちください。", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "タイトルに最大 %1 文字の制限があります。", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "自分のポストに評価することはできません。", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "スレッドのサムネイルが無効された", "invalid-file": "無効なファイル", "uploads-are-disabled": "アップロードが無効された", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "自分にチャットすることはできません!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/ja/groups.json b/public/language/ja/groups.json index d2314fdc29..5e301aa46a 100644 --- a/public/language/ja/groups.json +++ b/public/language/ja/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/ja/modules.json b/public/language/ja/modules.json index 1a30c49b7d..57daaef1c1 100644 --- a/public/language/ja/modules.json +++ b/public/language/ja/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Days", "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/ja/pages.json b/public/language/ja/pages.json index fda23c6d35..581b33a33d 100644 --- a/public/language/ja/pages.json +++ b/public/language/ja/pages.json @@ -5,7 +5,8 @@ "recent": "最新スレッド", "users": "登録したユーザー", "notifications": "通知", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "編集中 \"%1\"", "user.following": "%1がフォロー中", "user.followers": "%1のフォロワー", diff --git a/public/language/ja/reset_password.json b/public/language/ja/reset_password.json index 7e31c22720..e446d505e6 100644 --- a/public/language/ja/reset_password.json +++ b/public/language/ja/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "パスワードリセットのメールを送信しました", "invalid_email": "このメールアドレスは存在しません", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/ja/search.json b/public/language/ja/search.json index 9dad8b6eab..277c0a32bc 100644 --- a/public/language/ja/search.json +++ b/public/language/ja/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/ja/user.json b/public/language/ja/user.json index 9f94a46a32..a480f54f67 100644 --- a/public/language/ja/user.json +++ b/public/language/ja/user.json @@ -27,6 +27,7 @@ "chat": "チャット", "follow": "フォロー", "unfollow": "フォローしない", + "more": "More", "profile_update_success": "プロフィールを更新しました!", "change_picture": "画像を変更", "edit": "編集", @@ -47,7 +48,6 @@ "upload_picture": "画像をアップロード", "upload_a_picture": "画像をアップロード", "image_spec": "PNG、JPGかGIFフォーマットの画像をアップロードしてください。", - "max": "max。", "settings": "設定", "show_email": "メールアドレスを表示", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "メールアドレスを非表示", "hidden": "非表示", - "paginate_description": "スクロールでのページ自動ロードはしない", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "ページ毎のスレッド数", "posts_per_page": "ページ毎のポスト数", - "notification_sounds": "通知が来たとき音を流す", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/ko/category.json b/public/language/ko/category.json index 3518e3a3d1..f7ccdba720 100644 --- a/public/language/ko/category.json +++ b/public/language/ko/category.json @@ -5,5 +5,8 @@ "browsing": "이 주제를 읽고 있는 사용자", "no_replies": "답글이 없습니다.", "share_this_category": "이 카테고리를 공유", - "ignore": "관심 해제" + "watch": "Watch", + "ignore": "관심 해제", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/ko/error.json b/public/language/ko/error.json index 2c9de646c5..c9f81d74db 100644 --- a/public/language/ko/error.json +++ b/public/language/ko/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "사용자 이름이 너무 짧습니다.", "username-too-long": "사용자 이름이 너무 깁니다.", "user-banned": "차단된 사용자입니다.", - "user-too-new": "죄송합니다. 첫 번째 게시물은 %1 초 후에 작성할 수 있습니다", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "존재하지 않는 카테고리입니다.", "no-topic": "존재하지 않는 주제입니다.", "no-post": "존재하지 않는 게시물입니다.", @@ -35,17 +36,17 @@ "no-emailers-configured": "이메일 추가기능이 로드되지 않았으므로 테스트 메일을 발송할 수 없습니다.", "category-disabled": "비활성화된 카테고리입니다.", "topic-locked": "잠긴 주제입니다.", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "업로드가 끝날 때까지 기다려 주세요.", - "content-too-short": "게시물의 내용이 너무 짧습니다. 최소 %1자 이상이어야 합니다.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "제목이 너무 짧습니다. 최소 %1자 이상이어야 합니다.", - "title-too-long": "제목은 최대 %1자로 제한됩니다.", - "too-many-posts": "새 게시물 작성은 %1초 간격으로 제한됩니다 - 잠시 기다렸다가 작성해주세요.", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "파일의 크기는 최대 %1KB로 제한됩니다 - 더 작은 파일을 업로드하세요.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "자신의 게시물에는 투표할 수 없습니다.", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "이미 복원된 게시물입니다.", "topic-already-deleted": "이미 삭제된 주제입니다.", "topic-already-restored": "이미 복원된 주제입니다.", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "주제 섬네일이 이미 해제되었습니다.", "invalid-file": "올바르지 않은 파일입니다.", "uploads-are-disabled": "업로드는 비활성화되어 있습니다.", - "signature-too-long": "죄송합니다. 서명은 최대 %1자로 제한됩니다.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "자신과는 채팅할 수 없습니다.", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/ko/groups.json b/public/language/ko/groups.json index a3383c2ce2..6bc42b54fa 100644 --- a/public/language/ko/groups.json +++ b/public/language/ko/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/ko/modules.json b/public/language/ko/modules.json index fbdd01a7ae..5b25ee1201 100644 --- a/public/language/ko/modules.json +++ b/public/language/ko/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7일", "chat.thirty_days": "30일", "chat.three_months": "3개월", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1님이 %2에서 한 말:", "composer.user_said": "%1님의 말:", "composer.discard": "이 게시물을 지우겠습니까?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/ko/pages.json b/public/language/ko/pages.json index d5cd1ed4a4..d3197be70b 100644 --- a/public/language/ko/pages.json +++ b/public/language/ko/pages.json @@ -5,7 +5,8 @@ "recent": "최근 주제", "users": "사용자", "notifications": "알림", - "tags": "\"%1\"로 태그된 주제", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "%1님의 프로필 수정", "user.following": "%1님이 팔로우하는 사용자", "user.followers": "%1님을 팔로우하는 사용자", diff --git a/public/language/ko/reset_password.json b/public/language/ko/reset_password.json index 60fe18c8e5..6efe6dcf01 100644 --- a/public/language/ko/reset_password.json +++ b/public/language/ko/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "이메일이 발송되었습니다.", "invalid_email": "올바르지 않거나 가입되지 않은 이메일입니다.", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/ko/search.json b/public/language/ko/search.json index 3ec01cd868..44e5d45337 100644 --- a/public/language/ko/search.json +++ b/public/language/ko/search.json @@ -1,8 +1,8 @@ { "results_matching": "\"%2\"와 일치하는 %1개의 결과를 찾았습니다 (검색시간: %3초)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/ko/user.json b/public/language/ko/user.json index bf10413609..70ce30520a 100644 --- a/public/language/ko/user.json +++ b/public/language/ko/user.json @@ -27,6 +27,7 @@ "chat": "채팅", "follow": "팔로우", "unfollow": "팔로우 취소", + "more": "More", "profile_update_success": "성공적으로 프로필을 저장했습니다.", "change_picture": "사진 변경", "edit": "프로필 수정", @@ -47,7 +48,6 @@ "upload_picture": "사진 업로드", "upload_a_picture": "사진 업로드", "image_spec": "PNG, JPG, GIF 파일만 업로드할 수 있습니다.", - "max": "최대", "settings": "설정", "show_email": "이메일 공개", "show_fullname": "실명 공개", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "이메일 비공개", "hidden": "비공개", - "paginate_description": "주제와 게시물을 여러 페이지로 나누어 표시", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "페이지 당 주제 수", "posts_per_page": "페이지 당 게시물 수", - "notification_sounds": "알림을 수신하면 알림음을 재생", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "페이지 열기", - "open_links_in_new_tab": "외부 링크를 새 탭에서 열기", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "답글을 작성한 주제를 팔로우합니다.", - "follow_topics_you_create": "직접 작성한 주제를 팔로우합니다.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/lt/category.json b/public/language/lt/category.json index 3694a767a7..3fc832a8e4 100644 --- a/public/language/lt/category.json +++ b/public/language/lt/category.json @@ -5,5 +5,8 @@ "browsing": "naršo", "no_replies": "Niekas dar neatsakė", "share_this_category": "Pasidalinti šią kategoriją", - "ignore": "Nepaisyti" + "watch": "Watch", + "ignore": "Nepaisyti", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/lt/error.json b/public/language/lt/error.json index a1a25c65bc..b87aac771c 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "Šis forumas reikalauja patvirtinimo el. paštu prašome spausti čia el. adreso įrašymui", "email-confirm-failed": "Negalime patvirtinti jūsų el. adreso, prašom bandyti vėliau.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Slapyvardis per trumpas", "username-too-long": "Vartotojo vardas per ilgas", "user-banned": "Vartotojas užblokuotas", - "user-too-new": "Atsiprašome, bet jums reikia laukti 1% sekundes prieš patalpinant savo pirmąjį įrašą", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Tokios kategorijos nėra", "no-topic": "Tokios temos nėra", "no-post": "Tokio įrašo nėra", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategorija išjungta", "topic-locked": "Tema užrakinta", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Prašome palaukti kol bus baigti visi kėlimai į serverį", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Pavadinimas turėtų būti trumpesnis. Maksimalus leistinas ilgis- %1 simbolių.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Jūs negalite balsuoti už savo pranešimą", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "Šis įrašas jau atstatytas", "topic-already-deleted": "Ši tema jau ištrinta", "topic-already-restored": "Ši tema jau atkurta", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Temos paveikslėliai neleidžiami.", "invalid-file": "Klaidingas failas", "uploads-are-disabled": "Įkėlimai neleidžiami", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Jūs negalite susirašinėti su savimi!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "Išsiuntėte per daug pranešimų, kurį laiką prašome palaukti.", diff --git a/public/language/lt/groups.json b/public/language/lt/groups.json index 076154cf92..cc1b781426 100644 --- a/public/language/lt/groups.json +++ b/public/language/lt/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Grupės pavadinimas", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Aprašymas", "details.badge_preview": "Badge Preview", "details.change_icon": "Pakeisti paveikslėlį", diff --git a/public/language/lt/modules.json b/public/language/lt/modules.json index e6ddbe663e..1ad666d77e 100644 --- a/public/language/lt/modules.json +++ b/public/language/lt/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 dienos", "chat.thirty_days": "30 dienų", "chat.three_months": "3 mėnesiai", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 parašė į %2:", "composer.user_said": "%1 parašė:", "composer.discard": "Ar tikrai norite sunaikinti šį pranešimą?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/lt/pages.json b/public/language/lt/pages.json index c7cd3f490f..933fd00512 100644 --- a/public/language/lt/pages.json +++ b/public/language/lt/pages.json @@ -5,7 +5,8 @@ "recent": "Paskutinės temos", "users": "Registruoti vartotojai", "notifications": "Pranešimai", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Redaguojama \"%1\"", "user.following": "Vartotojas %1 seka", "user.followers": "Žmonės, kurie seka %1", diff --git a/public/language/lt/reset_password.json b/public/language/lt/reset_password.json index b95f3800be..870d8563d0 100644 --- a/public/language/lt/reset_password.json +++ b/public/language/lt/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Slaptažodžio atstatymas išsiųstas", "invalid_email": "Klaidingas arba neegzistuojantis el. pašto adresas!", "password_too_short": "Įvestas slaptažodis yra per trumpas, prašome pasirinkti kitą slaptažodį.", - "passwords_do_not_match": "Du slaptažodžiai, kuriuos įvedėte, nesutampa." + "passwords_do_not_match": "Du slaptažodžiai, kuriuos įvedėte, nesutampa.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/lt/search.json b/public/language/lt/search.json index b31715a6b4..34d00a95a7 100644 --- a/public/language/lt/search.json +++ b/public/language/lt/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "Atitikmenų nerasta", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Antraštės", "titles-posts": "Antraštės ir įrašai", "posted-by": "Parašė", diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 3146ac252f..4924eb3b4d 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -27,6 +27,7 @@ "chat": "Susirašinėti", "follow": "Sekti", "unfollow": "Nesekti", + "more": "More", "profile_update_success": "Profilis sėkmingai atnaujintas!", "change_picture": "Pakeisti paveikslėlį", "edit": "Redaguoti", @@ -47,7 +48,6 @@ "upload_picture": "Įkelti paveikslėlį", "upload_a_picture": "Įkelti paveikslėlį", "image_spec": "Galite įkelti tik PNG, JPG arba GIF failus", - "max": "maks.", "settings": "Nustatymai", "show_email": "Rodyti mano el. paštą viešai", "show_fullname": "Rodyti mano vardą ir pavardę", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Šis vartotojas dar neseka jokių temų", "email_hidden": "El. paštas paslėptas", "hidden": "paslėptas", - "paginate_description": "Temose ir kategorijose naudoti numeraciją puslapiams vietoje begalinės slinkties.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Temų puslapyje", "posts_per_page": "Pranešimų puslapyje", - "notification_sounds": "Groti garsą kai gaunate pranešimą.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Naršymo nustatymai", - "open_links_in_new_tab": "Atidaryti nuorodas naujame skirtuke?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Sekite temas, į kurias jūs atsakėte.", - "follow_topics_you_create": "Sekite jūsų sukurtas temas.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/ms/category.json b/public/language/ms/category.json index cc483ad763..fa77df77b5 100644 --- a/public/language/ms/category.json +++ b/public/language/ms/category.json @@ -1,9 +1,12 @@ { - "new_topic_button": "\nTopik Baru", - "guest-login-post": "Log in to post", - "no_topics": "Tiada topik dalam kategori ini.
Cuba menghantar topik yang baru?", + "new_topic_button": "Topik Baru", + "guest-login-post": "Log masuk untuk kirim", + "no_topics": "Tiada topik dalam kategori ini.
Cuba hantar topik yang baru?", "browsing": "melihat", "no_replies": "Tiada jawapan", "share_this_category": "Kongsi kategori ini", - "ignore": "Ignore" + "watch": "Melihat", + "ignore": "Abai", + "watch.message": "Anda sedang melihat kemaskini dari kategori ini", + "ignore.message": "Anda sedang mengabaikan kemaskini dari kategori ini" } \ No newline at end of file diff --git a/public/language/ms/email.json b/public/language/ms/email.json index 4c5ca1adde..385b7b84a2 100644 --- a/public/language/ms/email.json +++ b/public/language/ms/email.json @@ -1,28 +1,28 @@ { - "password-reset-requested": "Password Reset Requested - %1!", - "welcome-to": "Selemat datang ke %1", - "greeting_no_name": "Hello", - "greeting_with_name": "Hello %1", - "welcome.text1": "Thank you for registering with %1!", - "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", - "welcome.cta": "Click here to confirm your email address", - "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", - "reset.text2": "To continue with the password reset, please click on the following link:", - "reset.cta": "Click here to reset your password", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", - "digest.notifications": "You have unread notifications from %1:", - "digest.latest_topics": "Latest topics from %1", - "digest.cta": "Click here to visit %1", - "digest.unsub.info": "This digest was sent to you due to your subscription settings.", - "digest.no_topics": "There have been no active topics in the past %1", - "notif.chat.subject": "New chat message received from %1", - "notif.chat.cta": "Click here to continue the conversation", - "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", - "notif.post.cta": "Click here to read the full topic", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", - "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", - "unsub.cta": "Click here to alter those settings", + "password-reset-requested": "Permintaan set semula kata lalaun - %1!", + "welcome-to": "Selamat datang ke %1", + "greeting_no_name": "Salam", + "greeting_with_name": "Salam %1", + "welcome.text1": "Terima kasih kerana mendaftar dengan %1!", + "welcome.text2": "Untuk mengaktifkan akaun anda sepenuhnya, kami perlu mengesahkan bahawa anda memiliki alamat emel yang didaftarkan.", + "welcome.cta": "Klik sini untuk sahkan emel anda", + "reset.text1": "Kami menerima permintaan set semula kata laluan anda, kemungkinan kerana anda terlupa. Sekiranya tidak, sila abaikan emel ini.", + "reset.text2": "Untuk meneruskan dengan set semula kata laluan, sila klik pautan berikut:", + "reset.cta": "Klik sini untuk set semula kata laluan anda", + "reset.notify.subject": "Kata laluan berjaya ditukar", + "reset.notify.text1": "Pada %1, kata laluan anda berjaya ditukar.", + "reset.notify.text2": "Sekiranya anda tidak pernah melakukannya, sila hubungi pendtadbir / admin dengan segera.", + "digest.notifications": "Anda ada pemberitahuan yang belum dibaca, pemberitahuan dari %1:", + "digest.latest_topics": "Topik terkini dari %1", + "digest.cta": "Klik sini untuk melawat %1", + "digest.unsub.info": "Ringkasan ini dihantar berdasarkan tetapan langganan anda.", + "digest.no_topics": "Tiada topik yang aktif selama %1", + "notif.chat.subject": "Pesanan baru diterima dari %1", + "notif.chat.cta": "Klik sini untuk meneruskan perbualan", + "notif.chat.unsub.info": "Pemberitahuan sembang ini dihantar berdasarkan tetapan langganan anda.", + "notif.post.cta": "Klik sini untuk baca artikel penuh", + "notif.post.unsub.info": "Kiriman pemberitahuan ini dihantar berdasarkan tetapan langganan anda.", + "test.text1": "Ini adalah percubaan email untuk mengesahkan emailer ditetap dengan betul di NodeBB.", + "unsub.cta": "Klik sini untuk mengubah tetapan itu", "closing": "Terima Kasih!" } \ No newline at end of file diff --git a/public/language/ms/error.json b/public/language/ms/error.json index 23958cf5de..b0cc688b16 100644 --- a/public/language/ms/error.json +++ b/public/language/ms/error.json @@ -1,81 +1,83 @@ { - "invalid-data": "Invalid Data", - "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!", - "invalid-cid": "Invalid Category ID", - "invalid-tid": "Invalid Topic ID", - "invalid-pid": "Invalid Post ID", - "invalid-uid": "Invalid User ID", - "invalid-username": "Invalid Username", - "invalid-email": "Invalid Email", - "invalid-title": "Invalid title!", - "invalid-user-data": "Invalid User Data", - "invalid-password": "Password salah!", - "invalid-username-or-password": "Please specify both a username and password", - "invalid-search-term": "Invalid search term", - "invalid-pagination-value": "Invalid pagination value", - "username-taken": "Username taken", - "email-taken": "Email taken", - "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "invalid-data": "Data Tak Sah", + "not-logged-in": "Anda tidak log masuk.", + "account-locked": "Akaun anda telah dikunci untuk seketika", + "search-requires-login": "Pencarian perlukan akaun! Log masuk atau daftar!", + "invalid-cid": "Kategori ID Tak Sah", + "invalid-tid": "Topik ID Tak Sah", + "invalid-pid": "Kiriman ID Tak Sah", + "invalid-uid": "ID Pengguna Tak Sah", + "invalid-username": "Nama Pengguna Tak Sah", + "invalid-email": "Emel Tak Sah", + "invalid-title": "Tajuk Tak Sah!", + "invalid-user-data": "Data Pengguna Tak Sah", + "invalid-password": "Kata laluan salah!", + "invalid-username-or-password": "Sila tentukan kedua-dua nama pengguna dan kata laluan", + "invalid-search-term": "Terma pencarian tak sah", + "invalid-pagination-value": "Nilai halaman tak sah", + "username-taken": "Nama pengguna telah digunakan", + "email-taken": "Emel telah digunakan", + "email-not-confirmed": "Emel anda belum disahkan lagi, sila klik sini untuk mengesahkan emel anda.", + "email-not-confirmed-chat": "Anda tidak dibenarkan sembang sehingga emel disahkan, sila sahkan emel anda.", + "no-email-to-confirm": "Forum ini memerlukan pengesahan emel, sila klik sini untuk memasukkan emel", + "email-confirm-failed": "Kami tidak dapat memastikan emel anda, sila cuba lagi nanti", + "confirm-email-already-sent": "Pengesahan emel telah dihantar, sila tunggu %1 minit() untuk menghantar yang baru.", "username-too-short": "Nama pengunna terlalu pendek", - "username-too-long": "Username too long", - "user-banned": "User banned", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", - "no-category": "Category does not exist", - "no-topic": "Topic does not exist", - "no-post": "Post does not exist", - "no-group": "Group does not exist", - "no-user": "User does not exist", - "no-teaser": "Teaser does not exist", - "no-privileges": "You do not have enough privileges for this action.", - "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", - "category-disabled": "Category disabled", - "topic-locked": "Topic Locked", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", - "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", - "cant-vote-self-post": "You cannot vote for your own post", - "already-favourited": "You have already favourited this post", - "already-unfavourited": "You have already unfavourited this post", - "cant-ban-other-admins": "You can't ban other admins!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", - "group-name-too-short": "Group name too short", - "group-already-exists": "Group already exists", - "group-name-change-not-allowed": "Group name change not allowed", - "group-already-member": "You are already part of this group", - "group-needs-owner": "This group requires at least one owner", - "post-already-deleted": "This post has already been deleted", - "post-already-restored": "This post has already been restored", - "topic-already-deleted": "This topic has already been deleted", - "topic-already-restored": "This topic has already been restored", - "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", - "invalid-file": "Invalid File", - "uploads-are-disabled": "Uploads are disabled", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", - "cant-chat-with-yourself": "You can't chat with yourself!", - "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", - "too-many-messages": "You have sent too many messages, please wait awhile.", - "reputation-system-disabled": "Reputation system is disabled.", - "downvoting-disabled": "Downvoting is disabled", - "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", - "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", - "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", - "registration-error": "Registration Error", - "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "username-too-long": "Nama pengunna terlalu panjang", + "user-banned": "Pengguna diharamkan", + "user-too-new": "Maaf, anda dikehendaki menunggu %1 saat() sebelum membuat kiriman pertama anda", + "no-category": "Kategori tidak wujud", + "no-topic": "Topik tidak wujud", + "no-post": "Kiriman tidak wujud", + "no-group": "Kumpulan tidak wujud", + "no-user": "Pengguna tidak wujud", + "no-teaser": "Pengusik tidak wujud", + "no-privileges": "Anda tidak mempunyai cukup keistimewaan untuk perbuatan ini.", + "no-emailers-configured": "Tiada plug-in emel dimuatkan, jadi emel percubaan tidak dihantar", + "category-disabled": "Kategori dilumpuhkan", + "topic-locked": "Topik Dikunci", + "post-edit-duration-expired": "Anda hanya dibenarkan menyunting kiriman selepas %1 saat() berlalu", + "still-uploading": "Sila tunggu muatnaik untuk siap.", + "content-too-short": "Sila masukkan kiriman yang lebih panjang. Kiriman mesti mengandungi sekurang-kurangnya %1 aksara().", + "content-too-long": "Sila masukkan kiriman yang lebih ringkas. Kiriman mesti mengandungi tidak lebih %1 aksara().", + "title-too-short": "Sila masukkan tajuk yang lebih panjang. Tajuk mesti mengandungi sekurang-kurangnya %1 aksara().", + "title-too-long": "Sila masukkan tajuk yang lebih ringkas. Tajuk mesti mengandungi tidak lebih %1 aksara().", + "too-many-posts": "Anda hanya boleh mengirim sekali setiap %1 saat() - sila tunggu sebelum kiriman seterusnya", + "too-many-posts-newbie": "Sebagai pengguna baru, anda hanya boleh mengirim sekali setiap %1 saat() sehinnga anda mendapat %2 reputasi - sila tunggu sebelum kiriman seterusnya", + "tag-too-short": "Sila masukkan tag yang lebih panjang. Tag mesti mengandungi sekurang-kurangnya %1 aksara()", + "tag-too-long": "Sila masukkan tag yang lebih pendek. Tag mesti mengandungi tidak lebih %1 aksara()", + "file-too-big": "Maksimum saiz fail yang dibenarkan ialah %1 kB - sila muatnaik fail yang lebih kecil", + "cant-vote-self-post": "Anda tidak boleh mengundi kiriman sendiri", + "already-favourited": "Anda telah pun menggemari kiriman ini", + "already-unfavourited": "Anda telah pun nyah-gemar kiriman ini", + "cant-ban-other-admins": "Anda tidak boleh haramkan admin / pentadbir!", + "invalid-image-type": "Jenis imej tak sah. Jenis yang dibenarkan ialah: %1", + "invalid-image-extension": "Sambungan imej tak sah", + "invalid-file-type": "Jenis fail tak sah. Jenis fail yang dibenarkan ialah: %1", + "group-name-too-short": "Nama kumpulan terlalu pendek", + "group-already-exists": "Kumpulan telah wujud", + "group-name-change-not-allowed": "Pengubahan nama kumpulan tidak dibenarkan", + "group-already-member": "Anda telah pun menjadi ahli kumpulan ini", + "group-needs-owner": "Kumpulan ini memerlukan sekurang-kurangnya seorang pemilik", + "post-already-deleted": "Kiriman ini telah dipadam", + "post-already-restored": "Kiriman ini telah dipulihkan", + "topic-already-deleted": "Topik ini telah dipadam", + "topic-already-restored": "Kiriman ini telah dipulihkan", + "cant-purge-main-post": "Anda tidak boleh memadam, kiriman utama, sebaliknya sila pada topik", + "topic-thumbnails-are-disabled": "Topik kecil dilumpuhkan.", + "invalid-file": "Fail tak sah", + "uploads-are-disabled": "Muatnaik dilumpuhkan", + "signature-too-long": "Maaf, tandatangan tidak boleh lebih daripada %1 aksara().", + "cant-chat-with-yourself": "Anda tidak boleh sembang dengan diri sendiri!", + "chat-restricted": "Pengguna ini menyekat ruangan sembangnya. Dia hendaklah mengikut anda sebelum kalian dapat bersembang", + "too-many-messages": "Anda menghantar terlalu banyak pesanan, sila tunggu seketika.", + "reputation-system-disabled": "Sistem reputasi dilumpuhkan.", + "downvoting-disabled": "Undi turun dilumpuhkan", + "not-enough-reputation-to-downvote": "Anda tidak mempunyai reputasi mencukupi untuk mengundi turun kiriman ini", + "not-enough-reputation-to-flag": "Anda tidak mempunyai reputasi mencukupi untuk menanda kiriman ini", + "reload-failed": "NodeBB menemui masalah ketika muat semula: \"%1\". NodeBB akan terus melayan aset pelanggan sedia ada, tapi anda seharusnya undur perbuatan yang dilakukan sebelum muat semula.", + "registration-error": "Ralat pendaftaran.", + "parse-error": "Sesuatu tidak kena berlaku ketika menghuraikan repson pelayan (server)", + "wrong-login-type-email": "Sila guna emel anda untuk log masuk", + "wrong-login-type-username": "Sila guna nama pengguna anda untuk log masuk" } \ No newline at end of file diff --git a/public/language/ms/global.json b/public/language/ms/global.json index 438206d7ef..5b1c91d6bb 100644 --- a/public/language/ms/global.json +++ b/public/language/ms/global.json @@ -3,64 +3,64 @@ "search": "Cari", "buttons.close": "Tutup", "403.title": "Akses dinafikan", - "403.message": "You seem to have stumbled upon a page that you do not have access to.", - "403.login": "Perhaps you should try logging in?", + "403.message": "Anda tidak mempunyai kebenaran untuk melihat halaman ini", + "403.login": "Mungkin anda boleh cuba log masuk?", "404.title": "tidak dijumpai", - "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.", + "404.message": "Halaman yang diminta tidak wujud. Kembali ke halaman utama.", "500.title": "ralat dalaman", - "500.message": "Oops! ada yang tidak kena", + "500.message": "Oops! Macam ada yang tidak kena", "register": "Daftar", "login": "Log Masuk", "please_log_in": "Sila daftar masuk", "logout": "Log Keluar", "posting_restriction_info": "Kiriman terhad kepada pengguna berdaftar sahaja, Sila click disini untuk daftar masuk", - "welcome_back": "Welcome Back", - "you_have_successfully_logged_in": "Anda telah daftar keluar", - "save_changes": "simpan perubahan", + "welcome_back": "Selamat kembali", + "you_have_successfully_logged_in": "Anda telah berjaya log masuk", + "save_changes": "Simpan perubahan", "close": "Tutup", "pagination": "Mukasurat", "pagination.out_of": "%1 out of %2", - "pagination.enter_index": "Enter index", + "pagination.enter_index": "Masukkan indeks", "header.admin": "Pentadbir", - "header.recent": "terkini", + "header.recent": "Terkini", "header.unread": "Belum dibaca", "header.tags": "Tags", - "header.popular": "popular", - "header.users": "pengguna", - "header.groups": "Groups", - "header.chats": "chats", - "header.notifications": "makluman", + "header.popular": "Popular", + "header.users": "Pengguna", + "header.groups": "Kumpulan", + "header.chats": "Sembang", + "header.notifications": "Pemberitahuan", "header.search": "Cari", - "header.profile": "profil", - "notifications.loading": "Makluman sedang dimuatkan", - "chats.loading": "chats sedang dimuatkan", + "header.profile": "Profil", + "notifications.loading": "Pemberitahuan sedang dimuatkan", + "chats.loading": "Sembang sedang dimuatkan", "motd.welcome": "Selamat datang ke NodeBB, platfom perbincangan masa hadapan", "previouspage": "Laman sebelum", "nextpage": "Laman berikut", "alert.success": "berjaya", "alert.error": "ralat", "alert.banned": "Diharamkan", - "alert.banned.message": "You have just been banned, you will now be logged out.", + "alert.banned.message": "Amda baru sahaja diharamkan, anda sekarang akan di log keluar.", "alert.unfollow": "Anda tidak lagi mengikuti %1", "alert.follow": "Anda sekarang mengikuti %1", "online": "dalam talian", - "users": "Users", - "topics": "Topics", + "users": "Pengguna", + "topics": "Topik", "posts": "Kiriman", - "views": "paparan", - "reputation": "Reputation", - "read_more": "read more", - "posted_ago_by_guest": "posted %1 by Guest", - "posted_ago_by": "posted %1 by %2", - "posted_ago": "posted %1", - "posted_in_ago_by_guest": "posted in %1 %2 by Guest", - "posted_in_ago_by": "posted in %1 %2 by %3", - "posted_in_ago": "posted in %1 %2", - "replied_ago": "replied %1", - "user_posted_ago": "%1 posted %2", - "guest_posted_ago": "Guest posted %1", - "last_edited_by_ago": "last edited by %1 %2", - "norecentposts": "Tiada posting terkini", + "views": "Paparan", + "reputation": "Reputasi", + "read_more": "baca lafi", + "posted_ago_by_guest": "dikirim %1 oleh pelawat", + "posted_ago_by": "dikirim %1 oleh %2", + "posted_ago": "dikirim %1", + "posted_in_ago_by_guest": "dikirim pada %1 %2 oleh pelawat", + "posted_in_ago_by": "dikirim pada %1 %2 oleh %3", + "posted_in_ago": "dikirim pada %1 %2", + "replied_ago": "dibalas %1", + "user_posted_ago": "%1 mengirim %2", + "guest_posted_ago": "Pelawat mengirim %1", + "last_edited_by_ago": "terakhir disunting oleh %1 %2", + "norecentposts": "Tiada kiriman terkini", "norecenttopics": "Tiada topik terkini", "recentposts": "Kiriman terkini", "recentips": "IP berdaftar terkini", @@ -70,12 +70,12 @@ "offline": "Tidak ditalian", "email": "Emel", "language": "Bahasa", - "guest": "Guest", - "guests": "Guests", - "updated.title": "Forum Updated", - "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page.", - "privacy": "Privacy", - "follow": "Follow", - "unfollow": "Unfollow", - "delete_all": "Delete All" + "guest": "Pelawat", + "guests": "Pelawat", + "updated.title": "Forum Dikemaskini", + "updated.message": "Forum ini baru dshsjs dikemaskini ke versi terkini. Klik sini untuk segar semula halaman.", + "privacy": "Privasi", + "follow": "Ikut", + "unfollow": "Nyah-ikut", + "delete_all": "Padam Semua" } \ No newline at end of file diff --git a/public/language/ms/groups.json b/public/language/ms/groups.json index d2314fdc29..767f1e778f 100644 --- a/public/language/ms/groups.json +++ b/public/language/ms/groups.json @@ -1,34 +1,36 @@ { - "groups": "Groups", - "view_group": "View Group", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", - "details.title": "Group Details", - "details.members": "Member List", - "details.pending": "Pending Members", - "details.has_no_posts": "This group's members have not made any posts.", - "details.latest_posts": "Latest Posts", - "details.private": "Private", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "details.group_name": "Group Name", - "details.description": "Description", - "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", - "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", - "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", - "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "groups": "Kumpulan", + "view_group": "Lihat Kumpulan", + "owner": "Pemilik Kumpulan", + "new_group": "Buat Kumpulan Baru", + "no_groups_found": "Tiada kumpulan untuk dilihat", + "pending.accept": "Terima", + "pending.reject": "Tolak", + "cover-instructions": "Seret dan lepas gambar, lepas ke posisi, dan tekan Simpan", + "cover-change": "Ubah", + "cover-save": "Simpan", + "cover-saving": "Menyimpan", + "details.title": "Perincian Kumpulan", + "details.members": "Senarai Ahli", + "details.pending": "Ahli Menunggu", + "details.has_no_posts": "Kumpulan ahli kumpulan ini belum membuat sebarang kiriman.", + "details.latest_posts": "Kiriman Terkini", + "details.private": "Privasi", + "details.grant": "Pemberian/Pembatalan pemilikan", + "details.kick": "Tendang", + "details.owner_options": "Pentadbiran Kumpulan", + "details.group_name": "Nama Kumpulan", + "details.member_count": "Kiraan Ahli", + "details.creation_date": "Tarikh Dicipta", + "details.description": "Penerangan", + "details.badge_preview": "Pra-lihat Lencana", + "details.change_icon": "Tukar Ikon", + "details.change_colour": "Tukar Warna", + "details.badge_text": "Teks Lencana", + "details.userTitleEnabled": "Tunjuk Lencana", + "details.private_help": "Jika dibolehkan, menyertai kumpulan memerlukan kelulusan pemilik kumpulan", + "details.hidden": "Sembunyi", + "details.hidden_help": "Jika dibolehkan, kumpulan ini tidak akan dijumpai di senarai kumpulan, dan pengguna hendaklah di jemput secara manual", + "event.updated": "Perincian kumpulan telah dikemaskini", + "event.deleted": "Kumpulan \"%1\" telah dipadam" } \ No newline at end of file diff --git a/public/language/ms/login.json b/public/language/ms/login.json index 3f0d1e37ea..7536a7c973 100644 --- a/public/language/ms/login.json +++ b/public/language/ms/login.json @@ -1,11 +1,11 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", - "remember_me": "Ingat Saya?", + "username-email": "Nama pengguna / Emel", + "username": "Nama pengguna", + "email": "Emel", + "remember_me": "Ingatkan Saya", "forgot_password": "Lupa Kata Laluan?", - "alternative_logins": "Login Alternatif", - "failed_login_attempt": "Login gagal, sila cuba lagi.", - "login_successful": "Anda berjaya login!", - "dont_have_account": "Don't have an account?" + "alternative_logins": "Log Masuk Alternatif", + "failed_login_attempt": "Log masuk gagal, sila cuba lagi.", + "login_successful": "Anda berjaya log masuk!", + "dont_have_account": "Tiada akaun?" } \ No newline at end of file diff --git a/public/language/ms/modules.json b/public/language/ms/modules.json index 9cae9f7b74..a284cae6fe 100644 --- a/public/language/ms/modules.json +++ b/public/language/ms/modules.json @@ -1,22 +1,26 @@ { "chat.chatting_with": "Bercakap dengan ", - "chat.placeholder": "Type chat message here, press enter to send", + "chat.placeholder": "Taip pesanan di sini, tekan ENTER untuk hantar", "chat.send": "hantar", "chat.no_active": "Anda tiada pesanan yang aktif", "chat.user_typing": "%1 menaip", - "chat.user_has_messaged_you": "%1 has messaged you.", - "chat.see_all": "See all Chats", - "chat.no-messages": "Please select a recipient to view chat message history", - "chat.recent-chats": "Recent Chats", - "chat.contacts": "Contacts", - "chat.message-history": "Message History", - "chat.pop-out": "Pop out chat", - "chat.maximize": "Maximize", - "chat.seven_days": "7 Days", - "chat.thirty_days": "30 Days", - "chat.three_months": "3 Months", - "composer.user_said_in": "%1 said in %2:", - "composer.user_said": "%1 said:", - "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "chat.user_has_messaged_you": "%1 mesej anda.", + "chat.see_all": "Lihat semua Sembang", + "chat.no-messages": "Sila pilih penerima untuk lihat sejarah sembang", + "chat.recent-chats": "Sembang Terbaru", + "chat.contacts": "Hubungi", + "chat.message-history": "Sejarah Pesanan", + "chat.pop-out": "Pop keluar sembang", + "chat.maximize": "Memaksimum", + "chat.seven_days": "7 Hari", + "chat.thirty_days": "30 Hari", + "chat.three_months": "3 Bulan", + "composer.compose": "Tulis", + "composer.show_preview": "Pra-lihat", + "composer.hide_preview": "Sorok pra-lihat", + "composer.user_said_in": "%1 disebut di %2:", + "composer.user_said": "%1 berkata:", + "composer.discard": "Anda yakin untuk membuang kiriman ini?", + "composer.submit_and_lock": "Hantar dan Kunci", + "composer.toggle_dropdown": "Togol Kebawah" } \ No newline at end of file diff --git a/public/language/ms/notifications.json b/public/language/ms/notifications.json index e8fffb1cd8..0d8fccbef6 100644 --- a/public/language/ms/notifications.json +++ b/public/language/ms/notifications.json @@ -1,27 +1,27 @@ { "title": "pemberitahuan", "no_notifs": "Anda tiada pemberitahuan baru", - "see_all": "LIhat semua pemberitahuan", - "mark_all_read": "Mark all notifications read", - "back_to_home": "Back to %1", + "see_all": "Lihat semua pemberitahuan", + "mark_all_read": "Tandakan semua pemberitahuan sudah dibaca", + "back_to_home": "Kembali ke %1", "outgoing_link": "Sambungan luar", - "outgoing_link_message": "You are now leaving %1.", - "continue_to": "Continue to %1", - "return_to": "Return to %1", + "outgoing_link_message": "Anda sedang meninggalkan %1.", + "continue_to": "Sambung ke %1", + "return_to": "Kembali ke %1", "new_notification": "Pemberitahuan baru", - "you_have_unread_notifications": "Anda ada pemberitahuan yang belum dibaca", + "you_have_unread_notifications": "Ada pemberitahuan yang belum dibaca", "new_message_from": "Pesanan baru daripada %1", - "upvoted_your_post_in": "%1 has upvoted your post in %2.", - "moved_your_post": "%1 has moved your post.", - "moved_your_topic": "%1 has moved your topic.", - "favourited_your_post_in": "%1 has favourited your post in %2.", - "user_flagged_post_in": "%1 flagged a post in %2", - "user_posted_to": "%1 telah membalas posting kepada: %2", - "user_posted_topic": "%1 has posted a new topic: %2", - "user_mentioned_you_in": "%1 mentioned you in %2", - "user_started_following_you": "%1 started following you.", - "email-confirmed": "Email Confirmed", - "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", - "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", - "email-confirm-sent": "Confirmation email sent." + "upvoted_your_post_in": "%1 telah mengundi naik kiriman and di %2.", + "moved_your_post": "%1 telah memindahkan kiriman anda.", + "moved_your_topic": "%1 telah memindahkan topik anda.", + "favourited_your_post_in": "%1 menggemari kiriman and di %2.", + "user_flagged_post_in": "%1 menanda kiriman anda di %2", + "user_posted_to": "%1 telah membalas kiriman kepada: %2", + "user_posted_topic": "%1 membuka topik baru : %2", + "user_mentioned_you_in": "%1 sebut anda di %2", + "user_started_following_you": "%1 mula mengikut anda.", + "email-confirmed": "EMel Disahkan", + "email-confirmed-message": "Terima kasih kerana mengesahkan emel anda. Akaun anda telah diaktifkan sepenuhnya.", + "email-confirm-error-message": "Berlaku masalah semasa mengesahkan emel anda. Mungkin kod tidak sah atau tamat tempoh.", + "email-confirm-sent": "Pengesahan emel telah dihantar." } \ No newline at end of file diff --git a/public/language/ms/pages.json b/public/language/ms/pages.json index 439217122f..07d202fb98 100644 --- a/public/language/ms/pages.json +++ b/public/language/ms/pages.json @@ -5,16 +5,17 @@ "recent": "Topik Baru", "users": "Pengguna Berdaftar", "notifications": "Makluman", - "tags": "Topics tagged under \"%1\"", + "tags": "Tag", + "tag": "Topik ditag di bawwah \"%1\"", "user.edit": "Menyunting \"%1\"", - "user.following": "Pengguna yang %1 Ikuti", - "user.followers": "Pengguna yang Mengikuti %1", + "user.following": "Pengguna yang %1 diikuti", + "user.followers": "Pengguna yang Mengikut %1", "user.posts": "Kiriman dibuat oleh %1", "user.topics": "Topik dibuat oleh %1", - "user.groups": "%1's Groups", + "user.groups": "Kumpulan-kumpulan %1", "user.favourites": "Mesej Kegemaran %1", - "user.settings": "Tetapan pengguna", - "user.watched": "Topics watched by %1", - "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", - "maintenance.messageIntro": "Additionally, the administrator has left this message:" + "user.settings": "Tetapan Pengguna", + "user.watched": "Topik yand dilihat oleh %1", + "maintenance.text": "%1 sedang berada dalam mod pembaikpulihan. Sila cuba lagi nanti.", + "maintenance.messageIntro": "Tambahan, admin meninggalkan mesej ini :" } \ No newline at end of file diff --git a/public/language/ms/recent.json b/public/language/ms/recent.json index fe18961628..a2fc732a76 100644 --- a/public/language/ms/recent.json +++ b/public/language/ms/recent.json @@ -3,17 +3,17 @@ "day": "Hari", "week": "Minggu", "month": "Bulan", - "year": "Year", - "alltime": "All Time", + "year": "Tahun", + "alltime": "Selamanya", "no_recent_topics": "Tiada topik terkini", - "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "no_popular_topics": "Tiada topik popular.", + "there-is-a-new-topic": "Ada topik baru.", + "there-is-a-new-topic-and-a-new-post": "Ada topik baru dan kiriman baru.", + "there-is-a-new-topic-and-new-posts": "Ada topik baru dan %1 kiriman baru.", + "there-are-new-topics": "Ada %1 topik baru.", + "there-are-new-topics-and-a-new-post": "Ada %1 topik-topik dan kiriman-kiriman baru.", + "there-are-new-topics-and-new-posts": "Ada %1 topik baru dan %2 kiriman baru.", + "there-is-a-new-post": "Ada kiriman baru.", + "there-are-new-posts": "Ada %1 kiriman baru.", + "click-here-to-reload": "Klik sini untuk muat semula." } \ No newline at end of file diff --git a/public/language/ms/reset_password.json b/public/language/ms/reset_password.json index 2951ed5007..cb2fd6f183 100644 --- a/public/language/ms/reset_password.json +++ b/public/language/ms/reset_password.json @@ -1,16 +1,17 @@ { - "reset_password": "Menetapkan semula password", - "update_password": "Mengemaskini password", - "password_changed.title": "Password diubah", - "password_changed.message": "p>Password telah ditetapkan semula, Sila log masuk semula.", + "reset_password": "Menetapkan semula kata laluan", + "update_password": "Mengemaskini kata laluan", + "password_changed.title": "Kata laluan diubah", + "password_changed.message": "p>Kata laluan telah ditetapkan semula, Sila log masuk semula.", "wrong_reset_code.title": "Kod penetapan semula yang salah", - "wrong_reset_code.message": "Kod penetapan semua salah. Sila cuba lagi, atau mohon semula kod penetapan .", - "new_password": "Password baru", - "repeat_password": "Sahkan password", + "wrong_reset_code.message": "Kod penetapan semula salah. Sila cuba lagi, atau mohon semula kod penetapan .", + "new_password": "Kata laluan baru", + "repeat_password": "Sahkan kata laluan", "enter_email": "Sila masukkan alamat emel dan kami akan menghantar arahan untuk penetapan semula akaun anda", "enter_email_address": "Masukkan alamat emel", - "password_reset_sent": "Penetapan semula password telah dihantar", + "password_reset_sent": "Penetapan semula kata laluan telah dihantar", "invalid_email": "Emel yang tidak sah / Emel tidak wujud", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "Kata lauan terlalu pendek, sila pilih kata laluan yang lain", + "passwords_do_not_match": "Kedua-dua laluan yang dimasukkan tidak sepadan / tidak sama", + "password_expired": "Kata laluan telah tamat tempoh, pilih kata laluan baru" } \ No newline at end of file diff --git a/public/language/ms/search.json b/public/language/ms/search.json index 9dad8b6eab..8248f9adec 100644 --- a/public/language/ms/search.json +++ b/public/language/ms/search.json @@ -1,40 +1,40 @@ { - "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", - "no-matches": "No matches found", - "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order", - "save-preferences": "Save preferences", - "clear-preferences": "Clear preferences", - "search-preferences-saved": "Search preferences saved", - "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "results_matching": "%1 hasil sepadan \"%2\", (%3 saat)", + "no-matches": "Tiada padanan dijumpai", + "advanced-search": "Pencarian Lebih Mendalam", + "in": "Dalam", + "titles": "Tajuk", + "titles-posts": "Tajuk dan kiriman", + "posted-by": "Dikirim oleh", + "in-categories": "Dalam kategori", + "search-child-categories": "Cari anak kategori", + "reply-count": "Kira Balasan", + "at-least": "Sekurang-kurangnya", + "at-most": "Selebihnya", + "post-time": "Masa kiriman", + "newer-than": "Baru daripada", + "older-than": "Lama daripada", + "any-date": "Mana-mana masa", + "yesterday": "Semalam", + "one-week": "Seminggu", + "two-weeks": "Dua minggu", + "one-month": "Sebulan", + "three-months": "Tiga bulan", + "six-months": "Enam bulan", + "one-year": "Setahun", + "sort-by": "Susun mengikut", + "last-reply-time": "Masa balasan terakhir", + "topic-title": "Tajuk topik", + "number-of-replies": "Jumlah dibalas", + "number-of-views": "Jumlah dilihat", + "topic-start-date": "Tarikh topik mula", + "username": "Nama pengguna", + "category": "Kategori", + "descending": "Tertib menurun", + "ascending": "Tertib menaik", + "save-preferences": "Simpan butiran", + "clear-preferences": "Bersihkan butiran", + "search-preferences-saved": "Cari butiran yang disimpan", + "search-preferences-cleared": "Cari butiran yang diersihkan", + "show-results-as": "Tunjuk hasil sebagai" } \ No newline at end of file diff --git a/public/language/ms/success.json b/public/language/ms/success.json index fde8a77044..ca52454bf7 100644 --- a/public/language/ms/success.json +++ b/public/language/ms/success.json @@ -1,6 +1,6 @@ { - "success": "Success", - "topic-post": "You have successfully posted.", - "authentication-successful": "Authentication Successful", - "settings-saved": "Settings saved!" + "success": "Berjaya", + "topic-post": "Kiriman anda berjaya dihantar.", + "authentication-successful": "Pengesahan Berjaya", + "settings-saved": "Tetapan disimpan!" } \ No newline at end of file diff --git a/public/language/ms/tags.json b/public/language/ms/tags.json index c416d8d4ec..ad9c9350a7 100644 --- a/public/language/ms/tags.json +++ b/public/language/ms/tags.json @@ -1,7 +1,7 @@ { - "no_tag_topics": "There are no topics with this tag.", - "tags": "Tags", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", - "enter_tags_here_short": "Enter tags...", - "no_tags": "There are no tags yet." + "no_tag_topics": "Tiada topik untuk tag ini.", + "tags": "Tag", + "enter_tags_here": "Masukkan tag sini, masing-masing antara %1 dan %2 aksara.", + "enter_tags_here_short": "Masukkan tag ...", + "no_tags": "Belum ada tag." } \ No newline at end of file diff --git a/public/language/ms/topic.json b/public/language/ms/topic.json index e173c79263..fc1c334a0d 100644 --- a/public/language/ms/topic.json +++ b/public/language/ms/topic.json @@ -4,18 +4,18 @@ "topic_id_placeholder": "Masukkan ID topik", "no_topics_found": "Tiada topik yang ditemui", "no_posts_found": "Tiada kirim yang dijumpai", - "post_is_deleted": "This post is deleted!", + "post_is_deleted": "Kiriman ini di padam!", "profile": "Profil", - "posted_by": "Posted by %1", - "posted_by_guest": "Posted by Guest", + "posted_by": "Dikirim oleh %1", + "posted_by_guest": "Dikirim oleh pelawat", "chat": "Sembang", "notify_me": "Kekal dimaklumkan berkenaan respon dalam topik ini", "quote": "Petikan", "reply": "Balas", - "guest-login-reply": "Log in to reply", - "edit": "Edit", + "guest-login-reply": "Log masuk untuk balas", + "edit": "Sunting", "delete": "Padamkan", - "purge": "Purge", + "purge": "Singkirkan", "restore": "Pulihkan", "move": "Pindahkan", "fork": "Fork", @@ -23,77 +23,77 @@ "share": "Kongsi", "tools": "Perkakas", "flag": "Tanda", - "locked": "Locked", - "bookmark_instructions": "Clik disini untuk kembali ke posisi sebelumnya atau tutup untuk mengabaikan", - "flag_title": "Tanda posting ini untuk moderasi", + "locked": "Kunci", + "bookmark_instructions": "Klik disini untuk kembali ke posisi sebelumnya atau tutup untuk mengabaikan", + "flag_title": "Tanda kiriman ini untuk diselia", "flag_confirm": "Adakah anda pasti untuk menanda kiriman ini", - "flag_success": "Kiriman ini telah ditandakan untuk moderasi", - "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", - "following_topic.message": "Anda akan menerima makluman apabila ada kiriman kedalam topik ini", + "flag_success": "Kiriman ini telah ditandakan untuk diselia", + "deleted_message": "Topik ini telah dipadam. Hanya pengguna dengan kuasa pengurusan boleh melihatnya.", + "following_topic.message": "Anda akan menerima makluman apabila ada kiriman ke dalam topik ini", "not_following_topic.message": "Anda tidak lagi akan menerima makluman daripada topik ini", - "login_to_subscribe": "SIla daftar atau log masuk untuk melanggani topik ini", + "login_to_subscribe": "Sila daftar atau log masuk untuk melanggani topik ini", "markAsUnreadForAll.success": "Topik ditanda sebagai belum dibaca untuk semua", - "watch": "Saksikan", - "unwatch": "Unwatch", + "watch": "Lihat", + "unwatch": "Batal lihat", "watch.title": "Akan dimaklumkan sekiranya ada balasan dalam topik ini", - "unwatch.title": "Stop watching this topic", + "unwatch.title": "Berhenti melihat topik ini", "share_this_post": "Kongsi kiriman ini", - "thread_tools.title": "Topic Tools", + "thread_tools.title": "Perkakas Topik", "thread_tools.markAsUnreadForAll": "Tanda sebagai belum dibaca", "thread_tools.pin": "Pinkan topik", - "thread_tools.unpin": "Unpin topik", + "thread_tools.unpin": "Batalkan pin topik", "thread_tools.lock": "Kunci topik", "thread_tools.unlock": "Buka kekunci topik", "thread_tools.move": "Pindahkan topik", - "thread_tools.move_all": "Move All", + "thread_tools.move_all": "Pindahkan Semua", "thread_tools.fork": "Fork topik", "thread_tools.delete": "Padamkan topik", - "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", + "thread_tools.delete_confirm": "Anda yakin untuk padamkan topik ini?", "thread_tools.restore": "Pulihkan topik", - "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", - "thread_tools.purge": "Purge Topic", - "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", + "thread_tools.restore_confirm": "Anda yakin untuk pulihkan topik ini?", + "thread_tools.purge": "Singkirkan Topik", + "thread_tools.purge_confirm": "Anda yakin untuk singkirkan topik ini?", "topic_move_success": "Topik telah dipindahkan ke %1", "post_delete_confirm": "Adakah anda pasti untuk memadam kiriman ini?", "post_restore_confirm": "Adakah anda pasti untuk memulihkan kiriman ini?", - "post_purge_confirm": "Are you sure you want to purge this post?", + "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", "favourite": "Kegemaran", - "favourites": "Kegemaran - kegemaran", + "favourites": "Kegemaran-kegemaran", "favourites.has_no_favourites": "Anda tiada sebarang kegemaran, tandakan kiriman yang digemari untuk dilihat disini", - "loading_more_posts": "Memuatkan lagi kiriman ", + "loading_more_posts": "Memuatkan lagi kiriman", "move_topic": "Pindahkan topik", - "move_topics": "Move Topics", + "move_topics": "Pindahkan topik-topik", "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": "Clik kiriman yang anda hendak salin", + "fork_topic_instruction": "Klik kiriman yang anda hendak salin", "fork_no_pids": "Tiada kiriman yang dipilih", - "fork_success": "Successfully forked topic! Click here to go to the forked topic.", + "fork_success": "Berjaya menyalin topik. Klik sini untuk ke topik yang disalin.", "composer.title_placeholder": "Masukkan tajuk topik disini", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Nama", "composer.discard": "Abaikan", "composer.submit": "Hantar", - "composer.replying_to": "Replying to %1", + "composer.replying_to": "Balas ke %1", "composer.new_topic": "Topik baru", "composer.uploading": "Memuat naik ...", - "composer.thumb_url_label": "Pastekan gambaran URL", + "composer.thumb_url_label": "Tampalkan gambaran URL", "composer.thumb_title": "Letakkan gambaran kepada topik ini", "composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_file_label": "Atau muat naik fail", "composer.thumb_remove": "Bersihkan kawasan", "composer.drag_and_drop_images": "Seret dan lepaskan imej disini", - "more_users_and_guests": "%1 more user(s) and %2 guest(s)", - "more_users": "%1 more user(s)", - "more_guests": "%1 more guest(s)", - "users_and_others": "%1 and %2 others", - "sort_by": "Sort by", - "oldest_to_newest": "Oldest to Newest", - "newest_to_oldest": "Newest to Oldest", - "most_votes": "Most votes", - "most_posts": "Most posts" + "more_users_and_guests": "%1 lebih pengguna(-pengguna) dan %2 pelawat(-pelawat)", + "more_users": "%1 lebih pengguna(-pengguna)", + "more_guests": "%1 lebih pelawat(-pelawat)", + "users_and_others": "%1 dan %2 lain-lain", + "sort_by": "Susun ikut", + "oldest_to_newest": "Lama ke Baru", + "newest_to_oldest": "Baru ke Lama", + "most_votes": "Terbanyak Undi", + "most_posts": "Terbanyak Kiriman" } \ No newline at end of file diff --git a/public/language/ms/user.json b/public/language/ms/user.json index 89969347e8..305f15bdb8 100644 --- a/public/language/ms/user.json +++ b/public/language/ms/user.json @@ -2,12 +2,12 @@ "banned": "Diharamkan", "offline": "Tidak ditalian", "username": "Nama pengguna", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Tarikh Mula", + "postcount": "Jumlah Kiriman", "email": "Emel", - "confirm_email": "Confirm Email", - "delete_account": "Delete Account", - "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.", + "confirm_email": "Pastikan Emel", + "delete_account": "Padam Akaun", + "delete_account_confirm": "Anda yakin untuk paddam akaun anda?
Pebuatan ini tidak boleh diundur dan anda tidak boleh memulihkan sebarang data anda.

Masukkan nama pengguna anda untuk memastikan anda benar-benar ingin memadam akaun ini.", "fullname": "Nama penuh", "website": "Laman Web", "location": "Lokasi", @@ -18,65 +18,66 @@ "profile_views": "Lihat profil", "reputation": "Reputasi", "favourites": "Kegemaran", - "watched": "Watched", + "watched": "Melihat", "followers": "Pengikut", "following": "Mengikuti", "signature": "Tandatangan", "gravatar": "Gravatar", - "birthday": "Harijadi", + "birthday": "Tarikh lahir", "chat": "Bersembang", "follow": "Ikuti", - "unfollow": "Henti ikuti", + "unfollow": "Henti mengikuti", + "more": "Lagi", "profile_update_success": "Profil telah dikemaskini", "change_picture": "Tukar gambar", - "edit": "Edit", + "edit": "Sunting", "uploaded_picture": "Muatnaik gambak", "upload_new_picture": "Muatnaik gambar baru", - "upload_new_picture_from_url": "Upload New Picture From URL", - "current_password": "Password sekarang", - "change_password": "TUkar password", - "change_password_error": "Password salah!", - "change_password_error_wrong_current": "Password anda sekarang tidak sah", - "change_password_error_length": "Password terlalu pendek", - "change_password_error_match": "Password mesti padan", - "change_password_error_privileges": "You do not have the rights to change this password.", - "change_password_success": "Password dikemaskini", - "confirm_password": "Sahkan password", - "password": "password", + "upload_new_picture_from_url": "Muatnaik gambar baru dari URL", + "current_password": "Kata laluan sekarang", + "change_password": "Tukar kata laluan", + "change_password_error": "Kata laluan salah!", + "change_password_error_wrong_current": "Kata laluan anda sekarang tidak sah", + "change_password_error_length": "Kata laluan terlalu pendek", + "change_password_error_match": "Kata laluan mesti padan", + "change_password_error_privileges": "Anda tidak mempunyai kebenaran untuk mengubah kata laluan ini", + "change_password_success": "Kata laluan dikemaskini", + "confirm_password": "Sahkan kata laluan", + "password": "kata laluan", "username_taken_workaround": "Nama pengguna yang anda minta telah digunakan oleh orang lain, jadi kami telah mengubahsuaikannya sedikit. Anda kini dikenali sebagai %1", "upload_picture": "Muatnaik gambar", "upload_a_picture": "Muatnaik sekeping gambar", "image_spec": "Anda hanya boleh memuatnaikkan fail PNG, JPG atau GIF", - "max": "Maksimum", - "settings": "Penetapan", + "settings": "Tetapan", "show_email": "Tunjukkan emel saya", - "show_fullname": "Show My Full Name", - "restrict_chats": "Only allow chat messages from users I follow", - "digest_label": "Langgani berita", - "digest_description": "Langgani berita terkini untuk forum ini melalui emel (Makluman dan topik) menurut jadual yang ditetapkan", + "show_fullname": "Tunjukkan Nama Penuh", + "restrict_chats": "Hanya benarkan sembang mesej dari pengguna yang saya ikut sahaja", + "digest_label": "Langgan berita", + "digest_description": "Langgan berita terkini untuk forum ini melalui emel (Makluman dan topik) menurut jadual yang ditetapkan", "digest_off": "Tutup", "digest_daily": "Harian", "digest_weekly": "Mingguan", "digest_monthly": "Bulanan", - "send_chat_notifications": "Send an email if a new chat message arrives and I am not online", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "send_chat_notifications": "Hantar emel sekiranya ada mesej sembang baru dan saya di luar talian (offline)", + "send_post_notifications": "Hantarkan emel apabila topik yang saya langgan menerima balasan", + "settings-require-reload": "Sebahagian perubahan tetapan memerlukan segar semula. Klik sini untuk segar semula halaman ini.", "has_no_follower": "Pengguna ini tiada pengikut :(", "follows_no_one": "Pengguna ini tidak mengikuti sesiapa :(", - "has_no_posts": "Pengguna ini masih belum mengirim sebarang pos", - "has_no_topics": "Pengguna ini masih belum menghantar sebarang kiriman", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_posts": "Pengguna ini masih belum mengirim sebarang kiriman", + "has_no_topics": "Pengguna ini masih belum menghantar sebarang topik", + "has_no_watched_topics": "Pengguna ini masih belum melihat sebarang topik", "email_hidden": "Emel disembunyikan", "hidden": "disembunyikan", - "paginate_description": "Pecahkan topik dan pesanan adalah lebih baik daripada menggunakan scroll berterusan", + "paginate_description": "Gunakan muka surat untuk topik dan kiriman daripada penggunaan skroll infiniti", "topics_per_page": "Topik setiap muka", "posts_per_page": "Kiriman setiap muka", - "notification_sounds": "Mainkan muzik apabila anda menerima maklumbalas", - "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "notification_sounds": "Mainkan bunyi apabila anda mendapat pemberitahuan", + "browsing": "Melihat-lihat Tetapan", + "open_links_in_new_tab": "Buka pautan luar di tab yang baru", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Ikut topik yang anda balas", + "follow_topics_you_create": "Ikut topik yang anda buat", + "grouptitle": "Pilih nama kumpulan yang anda ingin tunjukkan", + "no-group-title": "Tiada nama kumpulan" } \ No newline at end of file diff --git a/public/language/ms/users.json b/public/language/ms/users.json index 26475bd746..5f373391d5 100644 --- a/public/language/ms/users.json +++ b/public/language/ms/users.json @@ -5,8 +5,8 @@ "search": "Cari", "enter_username": "Masukkan nama pengguna untuk carian", "load_more": "Muat lagi", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", - "online-only": "Online only", - "picture-only": "Picture only" + "users-found-search-took": "%1 pengguna dijumpai! Pencarian ambil masa %2 saat.", + "filter-by": "Saring dengan", + "online-only": "Atas talian sahaja", + "picture-only": "Gambar sahaja" } \ No newline at end of file diff --git a/public/language/nb/category.json b/public/language/nb/category.json index f8afafe14a..673aec04c3 100644 --- a/public/language/nb/category.json +++ b/public/language/nb/category.json @@ -5,5 +5,8 @@ "browsing": "leser", "no_replies": "Ingen har svart", "share_this_category": "Del denne kategorien", - "ignore": "Ignorer" + "watch": "Overvåk", + "ignore": "Ignorer", + "watch.message": "Du overvåker nå oppdateringer fra denne kategorien", + "ignore.message": "Du ignorerer nå oppdateringer fra denne kategorien" } \ No newline at end of file diff --git a/public/language/nb/error.json b/public/language/nb/error.json index 8706a9d569..a03b17a6b4 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -18,13 +18,14 @@ "username-taken": "Brukernavn opptatt", "email-taken": "E-post opptatt", "email-not-confirmed": "E-posten din har ikke blitt bekreftet enda, vennligst klikk for å bekrefte din e-post.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "Du kan ikke chatte før e-posten din er bekreftet, vennligst klikk her for å bekrefte e-postadressen.", "no-email-to-confirm": "Dette forumet krever at e-postbekreftelse, vennligst klikk her for å skrive inn en e-post", "email-confirm-failed": "Vi kunne ikke godkjenne e-posten din, vennligst prøv igjen senere.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Brukernavnet er for kort", "username-too-long": "Brukernavnet er for langt", "user-banned": "Bruker utestengt", - "user-too-new": "Beklager, du må vente %1 sekunder før du kan opprette ditt første innlegg.", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategorien eksisterer ikke.", "no-topic": "Emne eksisterer ikke", "no-post": "Innlegg eksisterer ikke", @@ -35,17 +36,17 @@ "no-emailers-configured": "Ingen e-post-tillegg er lastet, så ingen test e-post kunne bli sendt", "category-disabled": "Kategori deaktivert", "topic-locked": "Emne låst", - "post-edit-duration-expired": "Du har bare lov til å endre innlegg i %1 sekunder etter at det ble skrevet", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Vennligst vent til opplastingene blir fullført.", - "content-too-short": "Vennligst skriv et lengere innlegg. Innlegg må inneholde minst %1 tegn.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Vennligst skriv en lengere tittel. Titler må inneholde minst %1 tegn.", - "title-too-long": "Vennligst skriv en kortere tittel. Titler må inneholde minst %1 tegn.", - "too-many-posts": "Du kan bare poste et innlegg hvert %1 sekund - vennligst vent før du poster igjen", - "too-many-posts-newbie": "Som en ny bruker, kan du bare poste et innlegg hvert %1 sekund til du har tjent deg opp %2 i rykte - vennligst vent før du poster igjen", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Max tilatte filstørrelse er %1 kbs - vennligst last opp en mindre fil", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Du kan ikke stemme på ditt eget innlegg", "already-favourited": "Du har allerede favorittmerket dette innlegget", "already-unfavourited": "Du har allerede avfavorisert dette innlegget", @@ -62,13 +63,14 @@ "post-already-restored": "Dette innlegget har allerede blitt gjenopprettet", "topic-already-deleted": "Dette emnet har allerede blitt slettet", "topic-already-restored": "Dette emnet har allerede blitt gjenopprettet", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Emne-minatyrbilder har blitt deaktivert", "invalid-file": "Ugyldig fil", "uploads-are-disabled": "Opplastninger er deaktivert", - "signature-too-long": "Beklager, signaturen din kan ikke være lengre en %1 tegn.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Du kan ikke chatte med deg selv!", "chat-restricted": "Denne brukeren har begrenset sine chat-meldinger. De må følge deg før du kan chatte med dem", - "too-many-messages": "You have sent too many messages, please wait awhile.", + "too-many-messages": "Du har sendt for mange meldinger, vennligst vent en stund.", "reputation-system-disabled": "Ryktesystem er deaktivert.", "downvoting-disabled": "Nedstemming av deaktivert", "not-enough-reputation-to-downvote": "Du har ikke nok rykte til å nedstemme det innlegget", @@ -76,6 +78,6 @@ "reload-failed": "NodeBB støtte på et problem under lasting på nytt: \"%1\". NodeBB vil fortsette å servere eksisterende klientside ressureser, selv om du burde angre endringene du gjorde før du lastet på nytt.", "registration-error": "Feil under registrering", "parse-error": "Noe gikk feil under analysering av serversvar", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "wrong-login-type-email": "Vennligst bruk e-posten din for å logge inn", + "wrong-login-type-username": "Vennligst bruk brukernavnet ditt for å logge inn" } \ No newline at end of file diff --git a/public/language/nb/groups.json b/public/language/nb/groups.json index 6ff3a9b71f..0668648206 100644 --- a/public/language/nb/groups.json +++ b/public/language/nb/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kast ut", "details.owner_options": "Gruppeadministrasjon", "details.group_name": "Gruppenavn", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Beskrivelse", "details.badge_preview": "Forhåndsvisning av skilt", "details.change_icon": "Endre ikon", diff --git a/public/language/nb/modules.json b/public/language/nb/modules.json index e5656f83d1..e85add6129 100644 --- a/public/language/nb/modules.json +++ b/public/language/nb/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 dager", "chat.thirty_days": "30 dager", "chat.three_months": "3 måneder", + "composer.compose": "Komponer", + "composer.show_preview": "Vis forhåndsvisning", + "composer.hide_preview": "Skjul forhåndsvisning", "composer.user_said_in": "%1 sa i %2: ", "composer.user_said": "%1 sa: ", "composer.discard": "Er du sikker på at du vil forkaste dette innlegget?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Send og lås", + "composer.toggle_dropdown": "Veksle nedtrekksfelt" } \ No newline at end of file diff --git a/public/language/nb/pages.json b/public/language/nb/pages.json index 4f424ec5d6..6f0ab862c4 100644 --- a/public/language/nb/pages.json +++ b/public/language/nb/pages.json @@ -5,16 +5,17 @@ "recent": "Seneste emner", "users": "Registrerte brukere", "notifications": "Varsler", - "tags": "Emner merket \"%1\"", + "tags": "Tagger", + "tag": "Emner tagget under \"%1\"", "user.edit": "Endrer \"%1\"", "user.following": "Personer %1 følger", "user.followers": "Personer som følger %1", "user.posts": "Innlegg laget av %1", "user.topics": "Emner opprettet av %1", - "user.groups": "%1's Groups", + "user.groups": "%1 sine grupper", "user.favourites": "%1 sine favoritt-innlegg", "user.settings": "Brukerinnstillinger", - "user.watched": "Topics watched by %1", + "user.watched": "Innlegg overvåket av %|1", "maintenance.text": "%1 er for tiden under vedlikehold. Kom tilbake en annen gang.", "maintenance.messageIntro": "I tillegg har administratoren skrevet denne meldingen:" } \ No newline at end of file diff --git a/public/language/nb/reset_password.json b/public/language/nb/reset_password.json index ed6659abdf..f9cd7bdf72 100644 --- a/public/language/nb/reset_password.json +++ b/public/language/nb/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Passord-tilbakestilling sendt", "invalid_email": "Ugyldig e-post / e-post eksisterer ikke", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/nb/search.json b/public/language/nb/search.json index 2f06a88eb5..51c3d113da 100644 --- a/public/language/nb/search.json +++ b/public/language/nb/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 resultat(er) samsvarer med \"%2\", (%3 sekunder)", "no-matches": "Ingen matcher funnet", + "advanced-search": "Advanced Search", "in": "I", - "by": "Av", "titles": "Titler", "titles-posts": "Titler og innlegg", "posted-by": "Skapt av", diff --git a/public/language/nb/user.json b/public/language/nb/user.json index c64972929d..edb5f3d27d 100644 --- a/public/language/nb/user.json +++ b/public/language/nb/user.json @@ -27,6 +27,7 @@ "chat": "Chatt", "follow": "Følg", "unfollow": "Avfølg", + "more": "More", "profile_update_success": "Profilen ble oppdatert!", "change_picture": "Bytt bilde", "edit": "Endre", @@ -47,7 +48,6 @@ "upload_picture": "Last opp bilde", "upload_a_picture": "Last opp et bilde", "image_spec": "Du kan bare laste opp PNG, JPG eller GIF-filer", - "max": "maks.", "settings": "Innstillinger", "show_email": "Vis min e-post", "show_fullname": "Vis mitt fulle navn", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Denne brukeren overvåker ingen innlegg foreløpig.", "email_hidden": "E-post skjult", "hidden": "skjult", - "paginate_description": "Paginer tråder og innlegg istedet for å bruke uendelig skrolling.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Tråd per side", "posts_per_page": "Innlegg per side", - "notification_sounds": "Spill av en lyd når du mottar ett varsel.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Surfeinnstillinger", - "open_links_in_new_tab": "Åpne utgående linker i en ny fane?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Aktiver søk-i-emne", - "topic_search_help": "Hvis aktivert, vil i-emne-søk overstyre nettleseren sin standard sidesøk-oppførsel og tillate deg å søke gjennom hele emnet, i stedet for bare det som vises på skjermen.", - "follow_topics_you_reply_to": "Følg emner du svarer i.", - "follow_topics_you_create": "Følg emner du oppretter.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/nl/category.json b/public/language/nl/category.json index fe05622fbb..afb8eb21c8 100644 --- a/public/language/nl/category.json +++ b/public/language/nl/category.json @@ -5,5 +5,8 @@ "browsing": "verkennen", "no_replies": "Niemand heeft gereageerd", "share_this_category": "Deel deze categorie", - "ignore": "Negeren" + "watch": "Volgen", + "ignore": "Negeren", + "watch.message": "Je krijgt nu updates binnen van deze categorie", + "ignore.message": "Je krijgt geen updates meer binnen van deze categorie" } \ No newline at end of file diff --git a/public/language/nl/error.json b/public/language/nl/error.json index e6be514a63..984ed8e4a1 100644 --- a/public/language/nl/error.json +++ b/public/language/nl/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "U kunt helaas geen gebruik maken van chats tot uw email adres bevestigd is.", "no-email-to-confirm": "Dit forum vereist email bevestiging, klikt u alstublieft hier om uw email te vermelden", "email-confirm-failed": "Uw email kon helaas niet bevestigd worden, probeert u het alstublieft later nog eens.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Gebruikersnaam is te kort", "username-too-long": "Gebruikersnaam is te lang", "user-banned": "Gebruiker verbannen", - "user-too-new": "Onze excuses maar je moet nog %1 seconden wachten voordat je een bericht mag plaatsen", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Categorie bestaat niet", "no-topic": "Topic bestaat niet", "no-post": "Bericht bestaat niet", @@ -35,17 +36,17 @@ "no-emailers-configured": "Er zijn geen email plugins geladen, een test email kan dus niet verzonden worden", "category-disabled": "Categorie uitgeschakeld", "topic-locked": "Onderwerp gesloten", - "post-edit-duration-expired": "Het is niet toegestaan om berichten aan te passen tot %1 seconden na het plaatsen", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Heb even geduld totdat de alle bestanden geüpload zijn", - "content-too-short": "Maak de bericht alstublieft wat langer. Op z'n minst %1 karakters", - "content-too-long": "Maakt u het bericht alstublieft wat korter. Berichten mogen niet langer zijn dan %1 karakters.", - "title-too-short": "Maak de titel wat langer. Op z'n minst %1 karakters", - "title-too-long": "Maak de titel wat korter. Het kan niet langer zijn dan %1 karakters", - "too-many-posts": "Je kan eens in de %1 seconden een bericht aanmaken. Wacht alstublieft.", - "too-many-posts-newbie": "Als een nieuwe gebruiker kan je maar om de %1 seconden een bericht plaatsen vanwege je reputatie. Uw moet deze level van reputatie verdienen %2. Wacht alstublieft met het plaatsen van uw bericht.", - "tag-too-short": "Maakt u alstublieft de tag iets langer. Tags dienen minimaal %1 karakters te bevatten", - "tag-too-long": "Maakt u alstublieft de tag iets korter. Tags mogen maximaal %1 karakters bevatten", - "file-too-big": "De maximale bestandsgrootte is %1 kbs", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Je kan niet op je eigen berichten stemmen", "already-favourited": "U heeft al dit bericht in uw favorieten staan", "already-unfavourited": "U heeft al dit bericht uit uw favorieten gehaald", @@ -62,10 +63,11 @@ "post-already-restored": "Dit bericht is al hersteld", "topic-already-deleted": "Deze topic is al verwijderd", "topic-already-restored": "Deze topic is al hersteld", + "cant-purge-main-post": "Je kan het beginbericht niet verwijderen. Verwijder het onderwerp hiervoor.", "topic-thumbnails-are-disabled": "Onderwerp thumbnails zijn uitgeschakeld", "invalid-file": "Ongeldig bestand", "uploads-are-disabled": "Uploads zijn uitgeschakeld", - "signature-too-long": "Sorry, maar deze handtekening kan niet groter zijn dan %1 karakters!", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Je kan niet met jezelf chatten!", "chat-restricted": "Deze gebruiker heeft beperkingen gelegd op chatfunctie. Hun moeten jouw volgen voordat je met hun kan chatten", "too-many-messages": "U heeft teveel berichten verstuurd in een korte tijd. Wacht u alstublieft even.", diff --git a/public/language/nl/groups.json b/public/language/nl/groups.json index 67f21acd2b..8fbbbee137 100644 --- a/public/language/nl/groups.json +++ b/public/language/nl/groups.json @@ -20,6 +20,8 @@ "details.kick": "Verwijder", "details.owner_options": "Groeps Administratie", "details.group_name": "Groepsnaam", + "details.member_count": "Leden telling", + "details.creation_date": "Datum van het creëeren", "details.description": "Beschrijving", "details.badge_preview": "Badge Voorvertoning", "details.change_icon": "Icoon veranderen", diff --git a/public/language/nl/modules.json b/public/language/nl/modules.json index 4feffb4096..76dd6413a9 100644 --- a/public/language/nl/modules.json +++ b/public/language/nl/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Dagen", "chat.thirty_days": "30 Dagen", "chat.three_months": "3 Maanden", + "composer.compose": "samenstellen", + "composer.show_preview": "Laat voorbeeld zien", + "composer.hide_preview": "Verberg voorbeeld", "composer.user_said_in": "%1 zegt in %2:", "composer.user_said": "%1 zegt:", "composer.discard": "Weet u het zeker dat u dit bericht niet wilt plaatsen?", - "composer.submit_and_lock": "Plaatsen en vergrendelen" + "composer.submit_and_lock": "Plaatsen en vergrendelen", + "composer.toggle_dropdown": "pin menu" } \ No newline at end of file diff --git a/public/language/nl/pages.json b/public/language/nl/pages.json index 3f6abeb9af..f9db18bd4c 100644 --- a/public/language/nl/pages.json +++ b/public/language/nl/pages.json @@ -5,7 +5,8 @@ "recent": "Recente Onderwerpen", "users": "Geregistreerde Gebruikers", "notifications": "Notificaties", - "tags": "Onderwerpen getagd onder \"%1\"", + "tags": "Tags", + "tag": "Onderwerpen getagd onder \"%1\"", "user.edit": "\"%1\" aanpassen", "user.following": "Mensen %1 Volgt", "user.followers": "Mensen die %1 Volgen", diff --git a/public/language/nl/reset_password.json b/public/language/nl/reset_password.json index 1813eab2fe..87a167b975 100644 --- a/public/language/nl/reset_password.json +++ b/public/language/nl/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Wachtwoord Reset Verzonden", "invalid_email": "Fout Email Adres / Email Adres bestaat niet!", "password_too_short": "Het ingegeven wachtwoord is te kort. Kiest u alstublieft een ander wachtwoord.", - "passwords_do_not_match": "De twee wachtwoorden die u heeft ingegeven komen niet overeen." + "passwords_do_not_match": "De twee wachtwoorden die u heeft ingegeven komen niet overeen.", + "password_expired": "U wachtwoord is verlopen, kies een nieuw wachtwoord" } \ No newline at end of file diff --git a/public/language/nl/search.json b/public/language/nl/search.json index c104ef95d3..91d52f12a2 100644 --- a/public/language/nl/search.json +++ b/public/language/nl/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 resulta(a)ten was een match \"%2\", (%3 seconds)", "no-matches": "Geen matches gevonden", + "advanced-search": "Geavanceerd Zoeken", "in": "in", - "by": "door", "titles": "Titels", "titles-posts": "Titels en Berichten", "posted-by": "Geplaatst door", diff --git a/public/language/nl/user.json b/public/language/nl/user.json index 6726bf11ba..d30ff024a9 100644 --- a/public/language/nl/user.json +++ b/public/language/nl/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Volgen", "unfollow": "Ontvolgen", + "more": "Meer", "profile_update_success": "Uw profiel is succesvol geüpdatet", "change_picture": "Afbeelding Aanpassen", "edit": "Aanpassen", @@ -47,7 +48,6 @@ "upload_picture": "Afbeelding Uploaden", "upload_a_picture": "Upload een afbeelding", "image_spec": "Je mag alleen PNG, JPG, of GIF bestanden uploaden.", - "max": "max.", "settings": "Instellingen", "show_email": "Laat Mijn Email Zien", "show_fullname": "Laat mijn volledige naam zien", @@ -73,10 +73,11 @@ "posts_per_page": "Berichten per Pagina", "notification_sounds": "Speel een geluid af wanneer ik een notificatie ontvang.", "browsing": "Zoek Instellingen", - "open_links_in_new_tab": "Open de uitgaande links in een nieuw tabblad?", + "open_links_in_new_tab": "Open de uitgaande links in een nieuw tabblad", "enable_topic_searching": "Zet zoeken in het onderwerp aan", "topic_search_help": "Als het is ingeschakeld, dan zal het standaard zoeken overschrijven en zal je vanaf nu het gehele onderwerp kunnen doorzoeken ipv wat je standaard ziet.", "follow_topics_you_reply_to": "Volg de onderwerpen waarop u gereageerd heeft.", "follow_topics_you_create": "Volg de onderwerpen die u gecreëerd heeft.", - "grouptitle": "Selecteer de groepstitel die u wilt weergeven " + "grouptitle": "Selecteer de groepstitel die u wilt weergeven ", + "no-group-title": "Geen groepstitel" } \ No newline at end of file diff --git a/public/language/pl/category.json b/public/language/pl/category.json index a6e6ec1b0f..5efaa0f71c 100644 --- a/public/language/pl/category.json +++ b/public/language/pl/category.json @@ -5,5 +5,8 @@ "browsing": "przegląda", "no_replies": "Nikt jeszcze nie odpowiedział", "share_this_category": "Udostępnij tę kategorię", - "ignore": "Ignoruj" + "watch": "Watch", + "ignore": "Ignoruj", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/pl/error.json b/public/language/pl/error.json index e8f8f3dcdf..ca267996bc 100644 --- a/public/language/pl/error.json +++ b/public/language/pl/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "To forum wymaga weryfikacji przez email. Proszę kliknąć tutaj, aby wprowadzić adres.", "email-confirm-failed": "Nie byliśmy w stanie potwierdzić twojego email-a. Proszę spróbować później.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Nazwa użytkownika za krótka.", "username-too-long": "Zbyt długa nazwa użytkownika", "user-banned": "Użytkownik zbanowany", - "user-too-new": "Przepraszamy, powinieneś zaczekać %1 sekund przed pierwszym postem", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategoria nie istnieje", "no-topic": "Temat nie istnieje", "no-post": "Post nie istnieje", @@ -35,17 +36,17 @@ "no-emailers-configured": "Nie zainstalowano żadnego dodatku obsługującego e-mail, więc nie można wysłać testowej wiadomości.", "category-disabled": "Kategoria wyłączona.", "topic-locked": "Temat zamknięty", - "post-edit-duration-expired": "Możesz edytować posty przez %1 sekund po napisaniu.", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Poczekaj na pełne załadowanie", - "content-too-short": "Proszę wpisać dłuższy post. Posty powinny zawierać co najmniej %1 znaków.", - "content-too-long": "Proszę wpisać krótszy post. Posty nie mogą zawierać więcej niż %1 znaków.", - "title-too-short": "Proszę podać dłuższy tytuł. Tytuły powinny zawierać co najmniej %1 znaków.", - "title-too-long": "Wpisz krótszy tytuł, nie może być dłuższy niż %1 znaków.", - "too-many-posts": "Możesz wysyłać posty co %1 sekund - proszę poczekać", - "too-many-posts-newbie": "Jako nowy użytkownik, możesz wysyłać posty co %1 sekund, do chwili aż zbierzesz %2 reputacji - proszę poczekać przed ponownym wysłaniem posta", - "tag-too-short": "Proszę podać dłuższy tag. Tagi powinny zawierać co najmniej %1 znaków.", - "tag-too-long": "Proszę podać krótszy tag. Tagi nie mogą być dłuższe niż %1 znaków.", - "file-too-big": "Maksymalny dozwolony rozmiar pliku to %1 kb - proszę wybrać mniejszy plik", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Nie możesz głosować na własny post", "already-favourited": "Już polubiłeś ten post", "already-unfavourited": "Już przestałeś lubić ten post", @@ -62,10 +63,11 @@ "post-already-restored": "Ten post został już przywrócony", "topic-already-deleted": "Ten temat został już skasowany", "topic-already-restored": "Ten temat został już przywrócony", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Miniatury tematów są wyłączone", "invalid-file": "Błędny plik", "uploads-are-disabled": "Uploadowanie jest wyłączone", - "signature-too-long": "Przepraszamy, ale podpis nie może być dłuższy niż %1 znaków.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nie możesz rozmawiać ze sobą", "chat-restricted": "Ten użytkownik ograniczył swoje czaty. Musi Cię śledzić, zanim będziesz mógł z nim czatować.", "too-many-messages": "Wysłałeś zbyt wiele wiadomości, proszę poczekaj chwilę.", diff --git a/public/language/pl/groups.json b/public/language/pl/groups.json index b9c32948b9..89838d1bc4 100644 --- a/public/language/pl/groups.json +++ b/public/language/pl/groups.json @@ -20,6 +20,8 @@ "details.kick": "Wykop", "details.owner_options": "Administracja grupy", "details.group_name": "Nazwa grupy", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Opis", "details.badge_preview": "Podgląd plakietki", "details.change_icon": "Zmień ikonę", diff --git a/public/language/pl/modules.json b/public/language/pl/modules.json index f4a7f34c86..f4df7edb9c 100644 --- a/public/language/pl/modules.json +++ b/public/language/pl/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 dni", "chat.thirty_days": "30 dni", "chat.three_months": "3 miesiące", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 powiedział w %2:", "composer.user_said": "%1 powiedział:", "composer.discard": "Na pewno chcesz porzucić ten post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/pl/pages.json b/public/language/pl/pages.json index 91e9e5efe4..c566406d1a 100644 --- a/public/language/pl/pages.json +++ b/public/language/pl/pages.json @@ -5,7 +5,8 @@ "recent": "Ostatnie wątki", "users": "Zarejestrowani użytkownicy", "notifications": "Powiadomienia", - "tags": "Tematy oznaczone \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Edytowanie \"%1\"", "user.following": "Obserwowani przez %1", "user.followers": "Obserwujący %1", diff --git a/public/language/pl/reset_password.json b/public/language/pl/reset_password.json index 57b58f35e3..c152bb1aae 100644 --- a/public/language/pl/reset_password.json +++ b/public/language/pl/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Instrukcje zostały wysłane", "invalid_email": "Niepoprawny adres e-mail.", "password_too_short": "Wprowadzone hasło jest zbyt krótkie, proszę wybierz inne hasło.", - "passwords_do_not_match": "Wprowadzone hasła nie pasują do siebie" + "passwords_do_not_match": "Wprowadzone hasła nie pasują do siebie", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/pl/search.json b/public/language/pl/search.json index 52b4e5a137..44f5ca5023 100644 --- a/public/language/pl/search.json +++ b/public/language/pl/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 wyników pasujących do \"%2\", (%3 sekund)", "no-matches": "Nie znaleziono pasujących wyników", + "advanced-search": "Advanced Search", "in": "W", - "by": "Przez", "titles": "Tytuły", "titles-posts": "Tytuły i posty", "posted-by": "Napisane przez", diff --git a/public/language/pl/user.json b/public/language/pl/user.json index b0c031f94c..e8e495ae0c 100644 --- a/public/language/pl/user.json +++ b/public/language/pl/user.json @@ -27,6 +27,7 @@ "chat": "Rozmawiaj", "follow": "Śledź", "unfollow": "Przestań śledzić", + "more": "Więcej", "profile_update_success": "Profil został zaktualizowany pomyślnie!", "change_picture": "Zmień zdjęcie", "edit": "Edytuj", @@ -47,7 +48,6 @@ "upload_picture": "Prześlij zdjęcie", "upload_a_picture": "Prześlij zdjęcie", "image_spec": "Możesz przesłać tylko pliki PNG, JPG lub GIF.", - "max": "maks.", "settings": "Ustawienia", "show_email": "Wyświetlaj mój adres e-mail", "show_fullname": "Wyświetlaj moją pełną nazwę", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Ten użytkownik nie obserwował jeszcze żadnego wątku.", "email_hidden": "Adres e-mail ukryty", "hidden": "ukryty", - "paginate_description": "Użyj klasycznego trybu paginacji zamiast nieskończonego przewijania.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Wątków na stronę", "posts_per_page": "Postów na stronę", - "notification_sounds": "Odtwórz dźwięk po otrzymaniu powiadomienia.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Ustawienia szukania", - "open_links_in_new_tab": "Otwierać linki zewnętrzne w nowych kartach?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Odblokuj szukanie w wątku", - "topic_search_help": "Jeśli odblokowane, szukanie w wątku zastąpi domyślną funkcję szukania przeglądarki, pozwalając na wyszukiwanie fraz tylko w wątku, a nie na całej stronie.", - "follow_topics_you_reply_to": "Śledź tematy, w których piszesz.", - "follow_topics_you_create": "Śledź swoje tematy.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/pt_BR/category.json b/public/language/pt_BR/category.json index 7dc608219f..f996f22b23 100644 --- a/public/language/pt_BR/category.json +++ b/public/language/pt_BR/category.json @@ -5,5 +5,8 @@ "browsing": "navegando", "no_replies": "Ninguém respondeu", "share_this_category": "Compartilhar esta categoria", - "ignore": "Ignorar" + "watch": "Assistir", + "ignore": "Ignorar", + "watch.message": "Agora você está assistindo à atualizações desta categoria", + "ignore.message": "Agora você está ignorando as atualizações desta categoria" } \ No newline at end of file diff --git a/public/language/pt_BR/error.json b/public/language/pt_BR/error.json index cb218a3d43..a2518e7d1d 100644 --- a/public/language/pt_BR/error.json +++ b/public/language/pt_BR/error.json @@ -18,13 +18,14 @@ "username-taken": "Nome de usuário já existe", "email-taken": "Email já cadastrado", "email-not-confirmed": "O seu email ainda não foi confirmado, por favor clique aqui para confirmar seu email.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "Você não está habilitado a conversar até que seu email seja confirmado, por favor clique aqui para confirmar seu email.", "no-email-to-confirm": "Este fórum exige confirmação de email, por gentileza clique aqui para digitar um email", "email-confirm-failed": "Nós não pudemos confirmar seu email, por gentileza tente novamente mais tarde.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Nome de usuário muito curto", "username-too-long": "Nome de usuário muito longo", "user-banned": "Usuário banido", - "user-too-new": "Desculpe, é pedido que você aguarde %1 segundos antes de fazer o seu primeiro post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "A categoria não existe", "no-topic": "O tópico não existe", "no-post": "O post não existe", @@ -35,17 +36,17 @@ "no-emailers-configured": "Nenhum plugin de email foi carregado, por isso um email de teste não pôde ser enviado", "category-disabled": "Categoria desativada", "topic-locked": "Tópico Trancado", - "post-edit-duration-expired": "Você pode editar posts por %1 segundos após postar", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Aguarde a conclusão dos uploads.", - "content-too-short": "Por favor digite um post mais longo. Posts devem conter no mínimo %1 caracteres.", - "content-too-long": "Por favor entre com um post mais curto. Posts não podem ser maiores do que %1 caracteres.", - "title-too-short": "Por favor digite um título mais longo. Títulos devem conter no mínimo %1 caracteres.", - "title-too-long": "Por favor entre com um título mais curto; Títulos não podem ser maiores que %1 caracteres.", - "too-many-posts": "Você pode postar apenas uma vez a cada %1 segundos - por favor aguarde antes de postar novamente", - "too-many-posts-newbie": "Como novo usuário, você pode postar apenas uma vez a cada %1 segundos até que você tenha recebido reputação de %2 - por favor aguarde antes de postar novamente", - "tag-too-short": "Por favor digite uma tag mais longa. Tags devem conter pelo menos %1 caracteres", - "tag-too-long": "Por favor digite uma tag mais curta. Tags não podem ter mais do que %1 caracteres", - "file-too-big": "O tamanho máximo permitido de arquivo é %1 kbs - por favor faça upload de um arquivo menor", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Você não pode votar no seu próprio post", "already-favourited": "Você já adicionou este post aos favoritos", "already-unfavourited": "Você já removeu este post dos favoritos", @@ -62,10 +63,11 @@ "post-already-restored": "Este post já foi restaurado", "topic-already-deleted": "Esté tópico já foi deletado", "topic-already-restored": "Este tópico já foi restaurado", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Thumbnails para tópico estão desativados.", "invalid-file": "Arquivo Inválido", "uploads-are-disabled": "Uploads estão desativados", - "signature-too-long": "Desculpe, sua assinatura não pode ser maior que %1 caracteres.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Você não pode iniciar um chat consigo mesmo!", "chat-restricted": "Este usuário restringiu suas mensagens de chat. Eles devem seguir você antes que você possa conversar com eles", "too-many-messages": "Você enviou muitas mensagens, por favor aguarde um momento.", diff --git a/public/language/pt_BR/groups.json b/public/language/pt_BR/groups.json index 2246818345..d7e2aa0d32 100644 --- a/public/language/pt_BR/groups.json +++ b/public/language/pt_BR/groups.json @@ -20,6 +20,8 @@ "details.kick": "Chutar", "details.owner_options": "Administração do Grupo", "details.group_name": "Nome do Grupo", + "details.member_count": "Número de Membros", + "details.creation_date": "Data de Criação", "details.description": "Descrição", "details.badge_preview": "Visualização do Distintivo", "details.change_icon": "Mudar Ícone", diff --git a/public/language/pt_BR/modules.json b/public/language/pt_BR/modules.json index c899137cd6..734a60af2a 100644 --- a/public/language/pt_BR/modules.json +++ b/public/language/pt_BR/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Dias", "chat.thirty_days": "30 Dias", "chat.three_months": "3 Meses", + "composer.compose": "Compor", + "composer.show_preview": "Exibir Pré-visualização", + "composer.hide_preview": "Esconder Pré-visualização", "composer.user_said_in": "%1 disse em %2:", "composer.user_said": "%1 disse:", "composer.discard": "Tem certeza que deseja descartar essa postagem?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Envie e Tranque", + "composer.toggle_dropdown": "Alternar Dropdown" } \ No newline at end of file diff --git a/public/language/pt_BR/pages.json b/public/language/pt_BR/pages.json index edcc280f4b..5edce3eaac 100644 --- a/public/language/pt_BR/pages.json +++ b/public/language/pt_BR/pages.json @@ -5,7 +5,8 @@ "recent": "Tópicos Recentes", "users": "Usuários Registrados", "notifications": "Notificações", - "tags": "Tópicos com a tag \"%1\"", + "tags": "Tags", + "tag": "Tópicos com a tag \"%1\"", "user.edit": "Editando \"%1\"", "user.following": "Pessoas que %1 Segue", "user.followers": "Pessoas que Seguem %1", @@ -14,7 +15,7 @@ "user.groups": "%1's Grupos", "user.favourites": "Posts Favoritos de %1", "user.settings": "Configurações de Usuário", - "user.watched": "Topics watched by %1", + "user.watched": "Topicos acompanhados por %1", "maintenance.text": "%1 está atualmente sob manutenção. Por favor retorne em outro momento.", "maintenance.messageIntro": "Adicionalmente, o administrador deixou esta mensagem:" } \ No newline at end of file diff --git a/public/language/pt_BR/reset_password.json b/public/language/pt_BR/reset_password.json index 3aa6f341b4..6bdca20d39 100644 --- a/public/language/pt_BR/reset_password.json +++ b/public/language/pt_BR/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Reconfiguração de Senha Enviada", "invalid_email": "Email Inválido / Email não existe!", "password_too_short": "A senha entrada é muito curta, por favor escolha uma senha diferente.", - "passwords_do_not_match": "As duas senhas que você digitou não combinam." + "passwords_do_not_match": "As duas senhas que você digitou não combinam.", + "password_expired": "A sua senha expirou, por favor escolha uma nova senha" } \ No newline at end of file diff --git a/public/language/pt_BR/search.json b/public/language/pt_BR/search.json index 9c48e1dfc0..dd0dc6fa81 100644 --- a/public/language/pt_BR/search.json +++ b/public/language/pt_BR/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 resultado(s) contendo \"%2\", (%3 segundos)", "no-matches": "Nenhum resultado encontrado", + "advanced-search": "Pesquisa Avançada", "in": "Em", - "by": "Por", "titles": "Títulos", "titles-posts": "Títulos e Posts", "posted-by": "Postado por", @@ -13,7 +13,7 @@ "at-most": "No máximo", "post-time": "Hora da Postagem", "newer-than": "Mais novo que", - "older-than": "Mais velho que", + "older-than": "Mais antigo que", "any-date": "Qualquer data", "yesterday": "Ontem", "one-week": "Uma semana", diff --git a/public/language/pt_BR/tags.json b/public/language/pt_BR/tags.json index 1150d12f7f..90c59ec619 100644 --- a/public/language/pt_BR/tags.json +++ b/public/language/pt_BR/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Não há tópicos com esta tag.", "tags": "Tags", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Digite tags aqui, entre %1 e %2 caracteres cada.", "enter_tags_here_short": "Digite tags...", "no_tags": "Ainda não há tags." } \ No newline at end of file diff --git a/public/language/pt_BR/user.json b/public/language/pt_BR/user.json index be3033d1c3..677358d58d 100644 --- a/public/language/pt_BR/user.json +++ b/public/language/pt_BR/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Seguir", "unfollow": "Deixar de Seguir", + "more": "Mais", "profile_update_success": "O Perfil foi atualizado com sucesso!", "change_picture": "Alterar Foto", "edit": "Editar", @@ -47,7 +48,6 @@ "upload_picture": "Carregar Foto", "upload_a_picture": "Carregue uma Foto", "image_spec": "Você pode usar somente arquivos PNG, JPG ou GIF", - "max": "máx.", "settings": "Configurações", "show_email": "Mostrar Meu Email", "show_fullname": "Mostrar Meu Nome Completo", @@ -60,7 +60,7 @@ "digest_monthly": "Mensalmente", "send_chat_notifications": "Enviar-me um email se uma nova mensagem de chat chegar quando eu não estiver online.", "send_post_notifications": "Enviar um email quando respostas forem dadas à tópicos que eu assino", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Algumas mudanças de configuração exigem atualizar o navegador. Clique aqui para atualizar a página.", "has_no_follower": "Este usuário não possui seguidores :(", "follows_no_one": "Este usuário não está seguindo ninguém :(", "has_no_posts": "Este usuário não postou nada ainda.", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Este usuário ainda não acompanhou quaisquer tópicos.", "email_hidden": "Email Escondido", "hidden": "escondido", - "paginate_description": "Paginação de tópicos e posts ao invés de usar \"rolagem infinita\".", + "paginate_description": "Paginar tópicos ao invés de utilizar em vez de usar rolagem infinita.", "topics_per_page": "Tópicos por Página", "posts_per_page": "Posts por Página", - "notification_sounds": "Tocar um som quando você recebe notificação.", + "notification_sounds": "Tocar um som quando você receber uma notificação.", "browsing": "Configurações de Navegação", - "open_links_in_new_tab": "Abrir links externos em nova aba?", + "open_links_in_new_tab": "Abrir links externos em nova aba", "enable_topic_searching": "Habilitar Pesquisa dentro de Tópico", - "topic_search_help": "Se habilitado, a pesquisa dentro de tópico irá substituir o funcionamento padrão de pesquisa de página do navegador e permitir que você pesquise pelo tópico todo, ao invés de apenas o que é mostrado na tela.", - "follow_topics_you_reply_to": "Seguir tópicos que você responde.", - "follow_topics_you_create": "Seguir tópicos que você cria.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "Se habilitado, a pesquisa dentro do tópico irá substituir a pesquisa padrão do seu navegador. Assim, você poderá pesquisar pelo tópico inteiro, e não apenas pelo o que está sendo exibido na tela.", + "follow_topics_you_reply_to": "Seguir tópicos que você responde", + "follow_topics_you_create": "Seguir tópicos que você cria", + "grouptitle": "Escolha o título do grupo que você deseja exibir", + "no-group-title": "Sem título de grupo" } \ No newline at end of file diff --git a/public/language/ro/category.json b/public/language/ro/category.json index 733f5de0d7..6bc07f8f8c 100644 --- a/public/language/ro/category.json +++ b/public/language/ro/category.json @@ -5,5 +5,8 @@ "browsing": "navighează", "no_replies": "Nu a răspuns nimeni", "share_this_category": "Distribuie această categorie", - "ignore": "Ignoră" + "watch": "Watch", + "ignore": "Ignoră", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/ro/error.json b/public/language/ro/error.json index 1c60c0cf83..29d8d514b7 100644 --- a/public/language/ro/error.json +++ b/public/language/ro/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Numele de utilizator este prea scurt", "username-too-long": "Numele de utilizator este prea lung", "user-banned": "Utilizator banat", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "Nici un plugin pentru email nu a fost încărcat, așa că un email de test nu a fost trimis.", "category-disabled": "Categorie dezactivată", "topic-locked": "Subiect Închis", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Te rugăm să aștepți până se termină uploadul.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Te rugăm să introduci un titlu mai scurt. Titlurile nu pot fi mai lungi de %1 caractere.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Nu poți vota propriul tău mesaj", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Pictogramele pentru subiect sunt interzise.", "invalid-file": "Fișier invalid", "uploads-are-disabled": "Uploadurile sunt dezactivate", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nu poți conversa cu tine!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/ro/groups.json b/public/language/ro/groups.json index c41b17a0c5..2e45c9368c 100644 --- a/public/language/ro/groups.json +++ b/public/language/ro/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/ro/modules.json b/public/language/ro/modules.json index 585309d2ea..fcba85c55e 100644 --- a/public/language/ro/modules.json +++ b/public/language/ro/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Zile", "chat.thirty_days": "30 de zile", "chat.three_months": "3 Luni", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 a spus în %2:", "composer.user_said": "%1 a spus:", "composer.discard": "Ești sigur că vrei să renunți la acest mesaj?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/ro/pages.json b/public/language/ro/pages.json index 86deca16e0..a90cdc9d1a 100644 --- a/public/language/ro/pages.json +++ b/public/language/ro/pages.json @@ -5,7 +5,8 @@ "recent": "Subiecte Noi", "users": "Utilizatori înregistrați", "notifications": "Notificări", - "tags": "Subiecte tăguite cu \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Editează \"%1\"", "user.following": "Utilizatori urmăriți de %1", "user.followers": "Utilizatori care îl urmăresc pe %1", diff --git a/public/language/ro/reset_password.json b/public/language/ro/reset_password.json index d9edc5c856..15800e2b55 100644 --- a/public/language/ro/reset_password.json +++ b/public/language/ro/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Emailul pentru resetarea parolei a fost trimis", "invalid_email": "Adresă de email invalidă / Adresa de email nu există!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/ro/search.json b/public/language/ro/search.json index 54328cf585..6af7a31683 100644 --- a/public/language/ro/search.json +++ b/public/language/ro/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 rezultat(e) pentru \"%2\", (%3 secunde)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/ro/user.json b/public/language/ro/user.json index 7c5abe0840..c9c3554b44 100644 --- a/public/language/ro/user.json +++ b/public/language/ro/user.json @@ -27,6 +27,7 @@ "chat": "Conversație", "follow": "Urmărește", "unfollow": "Oprește urmărirea", + "more": "More", "profile_update_success": "Profilul tău a fost actualizat cu succes!", "change_picture": "Schimbă Poza", "edit": "Editează", @@ -47,7 +48,6 @@ "upload_picture": "Uploadează poză", "upload_a_picture": "Uploadează o poză", "image_spec": "Ai voie să uploadezi doar fișiere PNG, JPG sau GIF", - "max": "maxim.", "settings": "Setări", "show_email": "Arată adresa mea de email", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Adresă de email ascunsă", "hidden": "ascuns", - "paginate_description": "Paginează subiectele și mesajele în loc să folosești scrollul interminabil.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Subiecte pe pagină", "posts_per_page": "Mesaje pe pagină", - "notification_sounds": "Redă un sunet când primești o notificare.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Setări navigare", - "open_links_in_new_tab": "Deschide linkurile externe intr-un tab nou?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Urmărește subiectele în care ai răspuns.", - "follow_topics_you_create": "Urmărește subiectele care le creezi.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/ru/category.json b/public/language/ru/category.json index 4d70a82236..c584f9f4c6 100644 --- a/public/language/ru/category.json +++ b/public/language/ru/category.json @@ -5,5 +5,8 @@ "browsing": "просматривают", "no_replies": "Нет ответов", "share_this_category": "Поделиться этой категорией", - "ignore": "Игнорировать" + "watch": "Следить", + "ignore": "Игнорировать", + "watch.message": "Вы теперь следите за обновлениями из этой категории", + "ignore.message": "Вы теперь игнорируете обновления из этой категории" } \ No newline at end of file diff --git a/public/language/ru/error.json b/public/language/ru/error.json index e4be1447a2..99d893e336 100644 --- a/public/language/ru/error.json +++ b/public/language/ru/error.json @@ -18,13 +18,14 @@ "username-taken": "Имя пользователя занято", "email-taken": "Email занят", "email-not-confirmed": "Ваш email не подтвержден, нажмите для подтверждения.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "Вы не можете оставлять сообщения, пока Ваш email не подтверждён. Нажмите на это сообщение чтобы получить письмо повторно.", "no-email-to-confirm": "Этот форум требует подтверждения по E-mail. Нажмите здесь для ввода E-mail.", "email-confirm-failed": "Мы не можем подтвердить Ваш E-mail, попробуйте позже.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Слишком короткое имя пользователя", "username-too-long": "Имя пользователя слишком длинное", "user-banned": "Пользователь заблокирован", - "user-too-new": "Вы можете написать свой первый пост через %1 сек.", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Категория не существует", "no-topic": "Тема не существует", "no-post": "Сообщение не существует", @@ -35,17 +36,17 @@ "no-emailers-configured": "Не подключен ни один плагин для отправки почты, поэтому тестовый email не может быть отправлен", "category-disabled": "Категория отключена", "topic-locked": "Тема закрыта", - "post-edit-duration-expired": "Вы можете редактировать сообщение в течении %1 секунд(ы) после написания.", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Пожалуйста, подождите завершения загрузки.", - "content-too-short": "Пост должен содержать минимум %1 симв.", - "content-too-long": "Размер поста не должен превышать %1 символов. Пожалуйста, сделайте его короче.", - "title-too-short": "Заголовок должен содержать минимум %1 симв.", - "title-too-long": "Заголовок не может быть длиннее %1 символов.", - "too-many-posts": "Вы можете делать пост один раз в %1 сек.", - "too-many-posts-newbie": "Вы новый пользователь, поэтому можете делать пост раз в %1 сек., пока не заработаете %2 п. репутации.", - "tag-too-short": "Введите более длинный тэг. Тэги должны содержать как минимум %1 символа(ов).", - "tag-too-long": "Введите тэг покороче. Тэги должны быть короче %1 символов.", - "file-too-big": "Максимальный разрешенный размер файла - %1 Кбайт. Пожалуйста, загрузите файл меньшего размера.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Вы не можете проголосовать за Ваш пост", "already-favourited": "Вы уже добавили этот пост в избранное", "already-unfavourited": "Вы уже удалили этот пост из избранного", @@ -62,10 +63,11 @@ "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 симв.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Вы не можете общаться с самим собой", "chat-restricted": "Пользователь ограничил прием сообщений. Он должен подписаться на Вас, чтобы Вы могли вести переписку с ним.", "too-many-messages": "Вы отправили слишком много сообщений, подождите немного.", diff --git a/public/language/ru/groups.json b/public/language/ru/groups.json index 87d1f1c98e..b50974212e 100644 --- a/public/language/ru/groups.json +++ b/public/language/ru/groups.json @@ -15,11 +15,13 @@ "details.pending": "Заявки в группу", "details.has_no_posts": "Пользователями этой группы не публиковали никаких записей", "details.latest_posts": "Последние записи", - "details.private": "Частный (ая)", + "details.private": "Приватная", "details.grant": "Выдать/забрать администратора", "details.kick": "Исключить", "details.owner_options": "Настройки группы", "details.group_name": "Имя группы", + "details.member_count": "Количество участников", + "details.creation_date": "Дата создания", "details.description": "Описание", "details.badge_preview": "Предпросмотр бейджа", "details.change_icon": "Сменить иконку", diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index c707341577..770b9446f5 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 дней", "chat.thirty_days": "30 дней", "chat.three_months": "3 месяца", + "composer.compose": "Редактор", + "composer.show_preview": "Показать предпросмотр", + "composer.hide_preview": "Скрыть предпросмотр", "composer.user_said_in": "%1 сказал %2:", "composer.user_said": "%1 сказал:", "composer.discard": "Вы уверены, что хотите отказаться от этого поста?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Отправить и закрыть", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/ru/pages.json b/public/language/ru/pages.json index b7b35fa25e..7834c7951c 100644 --- a/public/language/ru/pages.json +++ b/public/language/ru/pages.json @@ -5,7 +5,8 @@ "recent": "Последние темы", "users": "Зарегистрированные пользователи", "notifications": "Уведомления", - "tags": "Темы с тегом \"%1\"", + "tags": "Теги", + "tag": "Темы с тегом \"%1\"", "user.edit": "Редактирование \"%1\"", "user.following": "%1 читает", "user.followers": "Читают %1", @@ -14,7 +15,7 @@ "user.groups": "Группы %1", "user.favourites": "Избранные сообщения %1", "user.settings": "Настройки", - "user.watched": "Topics watched by %1", + "user.watched": "Темы смотрели %1", "maintenance.text": "%1 в настоящее время на обслуживании. Пожалуйста, возвращайтесь позже.", "maintenance.messageIntro": "Администратор оставил сообщение:" } \ No newline at end of file diff --git a/public/language/ru/reset_password.json b/public/language/ru/reset_password.json index a2226d5e60..ee6b29edb7 100644 --- a/public/language/ru/reset_password.json +++ b/public/language/ru/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Пароль отправлен", "invalid_email": "Неверный Email / Email не существует!", "password_too_short": "Введенный пароль слишком короткий, пожалуйста, введите более длинный пароль.", - "passwords_do_not_match": "Введенные пароли не совпадают." + "passwords_do_not_match": "Введенные пароли не совпадают.", + "password_expired": "Ваш пароль устарел, пожалуйста выберите новый пароль" } \ No newline at end of file diff --git a/public/language/ru/search.json b/public/language/ru/search.json index 54886ccf15..8ca07bdccf 100644 --- a/public/language/ru/search.json +++ b/public/language/ru/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 результатов по фразе \"%2\", (%3 секунды) ", "no-matches": "Совпадений не найдено", + "advanced-search": "Расширенный поиск", "in": "В", - "by": "От", "titles": "Названия", "titles-posts": "Названия и сообщения", "posted-by": "Написано ", diff --git a/public/language/ru/tags.json b/public/language/ru/tags.json index e923cccf2c..8d0ced97f2 100644 --- a/public/language/ru/tags.json +++ b/public/language/ru/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Нет топиков с таким тегом.", "tags": "Теги", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Укажите теги здесь. %1-%2 символов. Нажимайте Enter после каждого тэга.", "enter_tags_here_short": "Введите теги...", "no_tags": "Здесь еще нет тегов." } \ No newline at end of file diff --git a/public/language/ru/user.json b/public/language/ru/user.json index 12797aef20..800be7cf79 100644 --- a/public/language/ru/user.json +++ b/public/language/ru/user.json @@ -27,6 +27,7 @@ "chat": "Чат", "follow": "Читать", "unfollow": "Не читать", + "more": "Ещё", "profile_update_success": "Профиль обновлен!", "change_picture": "Изменить фотографию", "edit": "Редактировать", @@ -47,7 +48,6 @@ "upload_picture": "Загрузить фотографию", "upload_a_picture": "Загрузить фотографию", "image_spec": "Поддерживаются только PNG, JPG или GIF файлы", - "max": "макс.", "settings": "Настройки", "show_email": "Показывать мой Email", "show_fullname": "Показывать Полное Имя", @@ -60,7 +60,7 @@ "digest_monthly": "За месяц", "send_chat_notifications": "Уведомлять на E-mail при поступлении нового сообщения чата, когда я оффлайн", "send_post_notifications": "Отправлять email, когда отвечают в темы, на которые я подписан(а)", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Для отображения некоторых изменений необходимо обновить страницу. Нажмите здесь чтобы продолжить.", "has_no_follower": "Этого пользователя никто не читает :(", "follows_no_one": "Этот пользователь никого не читает :(", "has_no_posts": "Этот пользователь еще ничего не написал.", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Этот пользователь еще не просмотрел ни одной темы", "email_hidden": "Email скрыт", "hidden": "скрыто", - "paginate_description": "Использовать пагинацию тем и постов вместо бесконечной прокрутки", + "paginate_description": "Разбивать по страницам топики и посты вместо того чтобы выводить бесконечным списком", "topics_per_page": "Тем на Странице", "posts_per_page": "Постов на Странице", - "notification_sounds": "Звук при получении уведомления", + "notification_sounds": "Воспроизводить звук во время получения уведомления", "browsing": "Настройки просмотра", - "open_links_in_new_tab": "Открывать ссылки, ведущие на другие сайты, в новой вкладке?", + "open_links_in_new_tab": "Открывать внешние ссылки в новых вкладках", "enable_topic_searching": "Активировать поиск внутри тем", - "topic_search_help": "Если включено, то стандартный \"Поиск на странице\" Вашего браузера будет осуществлять поиск по всей теме вместо одной её страницы.", - "follow_topics_you_reply_to": "Следить за темами, в которых Вы отвечали.", - "follow_topics_you_create": "Следить за темами, которые Вы создали.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "Если опция включена, поиск в теме будет осуществляться за счёт собственного поиска, который позволит искать во всей теме, а не только в загруженных сообщениях", + "follow_topics_you_reply_to": "Следить за темами в которых вы отвечаете", + "follow_topics_you_create": "Следить за темами которые вы создаёте", + "grouptitle": "Выберите бейдж группы для отображения", + "no-group-title": "Не показывать бейдж" } \ No newline at end of file diff --git a/public/language/sc/category.json b/public/language/sc/category.json index 306f0e2d04..25d11361e2 100644 --- a/public/language/sc/category.json +++ b/public/language/sc/category.json @@ -5,5 +5,8 @@ "browsing": "navighende", "no_replies": "Perunu at rispostu", "share_this_category": "Share this category", - "ignore": "Ignore" + "watch": "Watch", + "ignore": "Ignore", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/sc/error.json b/public/language/sc/error.json index 17465a20a3..867f331c3c 100644 --- a/public/language/sc/error.json +++ b/public/language/sc/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "User banned", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Category disabled", "topic-locked": "Topic Locked", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/sc/groups.json b/public/language/sc/groups.json index d2314fdc29..5e301aa46a 100644 --- a/public/language/sc/groups.json +++ b/public/language/sc/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/sc/modules.json b/public/language/sc/modules.json index c34b686af0..d22538edac 100644 --- a/public/language/sc/modules.json +++ b/public/language/sc/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Days", "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/sc/pages.json b/public/language/sc/pages.json index f087a77706..96de17a0e8 100644 --- a/public/language/sc/pages.json +++ b/public/language/sc/pages.json @@ -5,7 +5,8 @@ "recent": "Ùrtimas Arresonadas", "users": "Impitadores Registrados", "notifications": "Notìficas", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Acontzende \"%1\"", "user.following": "Gente chi %1 Sighit", "user.followers": "Gente chi Sighit %1", diff --git a/public/language/sc/reset_password.json b/public/language/sc/reset_password.json index d86b716c31..5662fb238f 100644 --- a/public/language/sc/reset_password.json +++ b/public/language/sc/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Còdighe pro Torrare a Assentare sa Password Imbiadu", "invalid_email": "Email Non Bàlida / Email chi no esistit!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/sc/search.json b/public/language/sc/search.json index 9dad8b6eab..277c0a32bc 100644 --- a/public/language/sc/search.json +++ b/public/language/sc/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/sc/user.json b/public/language/sc/user.json index 01f88d738a..8f73839294 100644 --- a/public/language/sc/user.json +++ b/public/language/sc/user.json @@ -27,6 +27,7 @@ "chat": "Tzarra", "follow": "Sighi", "unfollow": "Non sighes prus", + "more": "More", "profile_update_success": "Profile has been updated successfully!", "change_picture": "Muda Immàgine", "edit": "Acontza", @@ -47,7 +48,6 @@ "upload_picture": "Càrriga immàgine", "upload_a_picture": "Càrriga un'immàgine", "image_spec": "Podes carrigare isceti files PNG, JPG o GIF", - "max": "màssimu.", "settings": "Sèberos", "show_email": "Ammustra s'Email Mia", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Email Cuada", "hidden": "cuadu", - "paginate_description": "Partzi is arresonadas e arresonos in pàginas a su postu de impreare su iscorrimentu infinidu.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Arresonadas pro Pàgina", "posts_per_page": "Arresonos pro Pàgina", - "notification_sounds": "Play a sound when you receive a notification.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Browsing Settings", - "open_links_in_new_tab": "Open outgoing links in new tab?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/sk/category.json b/public/language/sk/category.json index 62ef9414d6..08035d7fdc 100644 --- a/public/language/sk/category.json +++ b/public/language/sk/category.json @@ -5,5 +5,8 @@ "browsing": "prehliada", "no_replies": "Nikdo ešte neodpovedal", "share_this_category": "zdielaj túto kategóriu", - "ignore": "Ignoruj" + "watch": "Watch", + "ignore": "Ignoruj", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/sk/error.json b/public/language/sk/error.json index 93c15ed553..aac7aeee03 100644 --- a/public/language/sk/error.json +++ b/public/language/sk/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Username too short", "username-too-long": "Username too long", "user-banned": "Užívateľ je zakázaný", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Category does not exist", "no-topic": "Topic does not exist", "no-post": "Post does not exist", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategória je znefunkčená.", "topic-locked": "Uzamknutá téma", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Prosím čakajte na dokončenie nahrávania", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Prosím uvedťe kratší názov. Názov nesmie byť dlhší ako 1 % znakov", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Nedá sa hlasovať za vlastný príspevok", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Nahľady tém sú znefunkčnené.", "invalid-file": "Neplatný súbor", "uploads-are-disabled": "Nahrávanie je znefunkčnené", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Nemôžete chatovat so samým sebou.", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/sk/groups.json b/public/language/sk/groups.json index d2314fdc29..5e301aa46a 100644 --- a/public/language/sk/groups.json +++ b/public/language/sk/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/sk/modules.json b/public/language/sk/modules.json index e8a242aba7..f4ebca7595 100644 --- a/public/language/sk/modules.json +++ b/public/language/sk/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Days", "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/sk/pages.json b/public/language/sk/pages.json index bbf7ebeebb..12bf4b817d 100644 --- a/public/language/sk/pages.json +++ b/public/language/sk/pages.json @@ -5,7 +5,8 @@ "recent": "Najnovšie príspevky", "users": "Prihlásení uživatelia", "notifications": "Notifikácie", - "tags": "Topics tagged under \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Uprav \"%1\"", "user.following": "Užívatelia %1 následujú", "user.followers": "Užívatelia následujúci %1", diff --git a/public/language/sk/reset_password.json b/public/language/sk/reset_password.json index 2391c09cae..cafdfccd4e 100644 --- a/public/language/sk/reset_password.json +++ b/public/language/sk/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Obnova hesla odoslaná", "invalid_email": "Zlý email / Email neexistuje!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/sk/search.json b/public/language/sk/search.json index 9dad8b6eab..277c0a32bc 100644 --- a/public/language/sk/search.json +++ b/public/language/sk/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/sk/user.json b/public/language/sk/user.json index db1541864f..da71890f8c 100644 --- a/public/language/sk/user.json +++ b/public/language/sk/user.json @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Nasleduj", "unfollow": "Nenasledovať", + "more": "More", "profile_update_success": "Profil bol úspešne aktualizovaný!", "change_picture": "Zmeniť obrázok", "edit": "Upraviť", @@ -47,7 +48,6 @@ "upload_picture": "Nahrať obrázok", "upload_a_picture": "Nahrať obrázok", "image_spec": "Môžeš pridať len obrázky typu PNG, JPG alebo GIF", - "max": "max.", "settings": "Nastavenia", "show_email": "Zobrazovať môj email v profile", "show_fullname": "Show My Full Name", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Skrytý email", "hidden": "skrytý", - "paginate_description": "Stránkuj témy a príspevky miesto používania nekonečného posúvania.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Témy na stranu", "posts_per_page": "Príspevkov na stranu", - "notification_sounds": "Prehraj zvuk ked príde notifikácia", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Hľadaj v nadstaveniach", - "open_links_in_new_tab": "Otvoriť tieto odkazy v novom tabe ?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/sv/category.json b/public/language/sv/category.json index 41b49a95db..722a8d0c45 100644 --- a/public/language/sv/category.json +++ b/public/language/sv/category.json @@ -5,5 +5,8 @@ "browsing": "läser", "no_replies": "Ingen har svarat", "share_this_category": "Dela den här kategorin", - "ignore": "Ignorera" + "watch": "Watch", + "ignore": "Ignorera", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/sv/error.json b/public/language/sv/error.json index 99879a3388..bfd4e763eb 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Användarnamnet är för kort", "username-too-long": "Användarnamnet är för långt", "user-banned": "Användare bannlyst", - "user-too-new": "Tyvärr, du måste vänta %1 sekunder innan du kan skapa ditt första inlägg", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategorin hittades inte", "no-topic": "Ämnet hittades inte", "no-post": "Inlägget hittades inte", @@ -35,17 +36,17 @@ "no-emailers-configured": "Inga tillägg för epostadress har laddats, så något textmeddelande kunde inte skickas", "category-disabled": "Kategorin inaktiverad", "topic-locked": "Ämnet låst", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Vänta medan uppladdningen slutförs.", - "content-too-short": "Skriv ett längre inlägg. Inlägget måste ha minst %1 tecken.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Skriv en längre rubrik. Rubriken måste ha minst %1 tecken.", - "title-too-long": "Skriv in en kortare rubrik. Rubriker får inte vara längre än %1 tecken.", - "too-many-posts": "Du kan endast skapa inlägg var %1:e sekund - vänta ett tag innan du försöker skapa ett nytt inlägg", - "too-many-posts-newbie": "Som en ny användare kan du endast skapa nya inlägg var %1:e sekund tills du har %2 förtroende - vänta ett tag innan du försöker skapa ett nytt inlägg", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximal filstorlek är %1 kbs - var god ladda upp en mindre fil", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Du kan inte rösta på ditt eget inlägg.", "already-favourited": "Du har redan favoriserat det här inlägget", "already-unfavourited": "Du har redan avfavoriserat det här inlägget", @@ -62,10 +63,11 @@ "post-already-restored": "Inlägget är redan återställt", "topic-already-deleted": "Ämnet är redan raderat", "topic-already-restored": "Ämnet är redan återställt", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Miniatyrbilder för ämnen är inaktiverat", "invalid-file": "Ogiltig fil", "uploads-are-disabled": "Uppladdningar är inaktiverat", - "signature-too-long": "Signaturer kan tyvärr inte vara längre än %1 tecken.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Du kan inte chatta med dig själv.", "chat-restricted": "Denna användaren har begränsat sina chatt-meddelanden. Användaren måste följa dig innan ni kan chatta med varann", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/sv/groups.json b/public/language/sv/groups.json index 8474322fa4..bf3aaa470d 100644 --- a/public/language/sv/groups.json +++ b/public/language/sv/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/sv/modules.json b/public/language/sv/modules.json index 05d12e9247..008af93bc0 100644 --- a/public/language/sv/modules.json +++ b/public/language/sv/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Dagar", "chat.thirty_days": "30 Dagar", "chat.three_months": "3 Månader", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 sa i %2:", "composer.user_said": "%1 sa:", "composer.discard": "Är du säker på att du vill förkasta det här inlägget?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/sv/pages.json b/public/language/sv/pages.json index 61fb23b776..94c6a26d0e 100644 --- a/public/language/sv/pages.json +++ b/public/language/sv/pages.json @@ -5,7 +5,8 @@ "recent": "Senaste ämnena", "users": "Registrerade användare", "notifications": "Notiser", - "tags": "Ämnen märkta med \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Ändrar \"%1\"", "user.following": "Personer %1 Följer", "user.followers": "Personer som följer %1", diff --git a/public/language/sv/reset_password.json b/public/language/sv/reset_password.json index a3771a31b4..17c4447925 100644 --- a/public/language/sv/reset_password.json +++ b/public/language/sv/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Lösenordsåterställning skickad", "invalid_email": "Felaktig epost / Epost finns inte!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/sv/search.json b/public/language/sv/search.json index 4418f62e13..7822eeae96 100644 --- a/public/language/sv/search.json +++ b/public/language/sv/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 resultat matchar \"%2\", (%3 sekunder)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/sv/user.json b/public/language/sv/user.json index 14ceb0336a..2702388846 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -27,6 +27,7 @@ "chat": "Chatta", "follow": "Följ", "unfollow": "Sluta följ", + "more": "More", "profile_update_success": "Profilen uppdaterades.", "change_picture": "Ändra bild", "edit": "Ändra", @@ -47,7 +48,6 @@ "upload_picture": "Ladda upp bild", "upload_a_picture": "Ladda upp en bild", "image_spec": "Du får bara ladda upp PNG, JPG eller GIF-filer", - "max": "max", "settings": "Inställningar", "show_email": "Visa min epost", "show_fullname": "Visa Fullständigt Namn", @@ -68,15 +68,16 @@ "has_no_watched_topics": "This user didn't watch any topics yet.", "email_hidden": "Epost dold", "hidden": "dold", - "paginate_description": "Gör så att ämnen och inlägg visas som sidor istället för oändlig scroll.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Ämnen per sida", "posts_per_page": "Inlägg per sida", - "notification_sounds": "Spela ett ljud när du får en notis.", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Inställning för bläddring", - "open_links_in_new_tab": "Öppna utgående länkar på ny flik?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Aktivera Sökning Inom Ämne", - "topic_search_help": "Om aktiverat kommer sökning inom ämne överskrida webbläsarens vanliga sid-sökfunktion och tillåta dig att söka genom hela ämnet istället för det som endast visas på skärmen.", - "follow_topics_you_reply_to": "Följ ämnen so du svarat på.", - "follow_topics_you_create": "Följ ämnen du skapat.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/th/category.json b/public/language/th/category.json index e5d6b1e0fc..bf35101ba9 100644 --- a/public/language/th/category.json +++ b/public/language/th/category.json @@ -5,5 +5,8 @@ "browsing": "เรียกดู", "no_replies": "ยังไม่มีใครตอบ", "share_this_category": "แชร์ Category นี้", - "ignore": "ไม่ต้องสนใจอีก" + "watch": "Watch", + "ignore": "ไม่ต้องสนใจอีก", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 7a46ebaa65..1c33a57db0 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "Forum นี้ต้องการการยืนยันอีเมล กรุณากดที่นี่เพื่อระบุอีเมล", "email-confirm-failed": "เราไม่สามารถยืนยันอีเมลของคุณ ณ ขณะนี้ กรุณาลองใหม่อีกครั้งภายหลัง", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "ชื่อบัญชีผู้ใช้ สั้นเกินไป", "username-too-long": "ชื่อบัญชีผู้ใช้ ยาวเกินไป", "user-banned": "User banned", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "ยังไม่มี Category นี้", "no-topic": "ยังไม่มี Topic นี้", "no-post": "ยังไม่มี Post นี้", @@ -35,17 +36,17 @@ "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Category นี้ถูกปิดการใช้งานแล้ว", "topic-locked": "Topic Locked", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Please wait for uploads to complete.", - "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.", - "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.", - "too-many-posts": "You can only post once every %1 seconds - please wait before posting again", - "too-many-posts-newbie": "As a new user, you can only post once every %1 seconds until you have earned %2 reputation - please wait before posting again", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "You cannot vote for your own post", "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", @@ -62,10 +63,11 @@ "post-already-restored": "This post has already been restored", "topic-already-deleted": "This topic has already been deleted", "topic-already-restored": "This topic has already been restored", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.", "invalid-file": "Invalid File", "uploads-are-disabled": "Uploads are disabled", - "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "You can't chat with yourself!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/th/groups.json b/public/language/th/groups.json index be94efdf0b..089ba7fa93 100644 --- a/public/language/th/groups.json +++ b/public/language/th/groups.json @@ -20,6 +20,8 @@ "details.kick": "เตะออก", "details.owner_options": "การจัดการ Group", "details.group_name": "ชื่อ Group", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "คำอธิบาย", "details.badge_preview": "Badge Preview", "details.change_icon": "เปลี่ยนไอคอน", diff --git a/public/language/th/modules.json b/public/language/th/modules.json index 21cb9a564d..7af8cf639b 100644 --- a/public/language/th/modules.json +++ b/public/language/th/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Days", "chat.thirty_days": "30 Days", "chat.three_months": "3 Months", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", "composer.discard": "Are you sure you wish to discard this post?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/th/notifications.json b/public/language/th/notifications.json index 53d7dc8ea7..f7383690c5 100644 --- a/public/language/th/notifications.json +++ b/public/language/th/notifications.json @@ -8,8 +8,8 @@ "outgoing_link_message": "You are now leaving %1.", "continue_to": "Continue to %1", "return_to": "Return to %1", - "new_notification": "New Notification", - "you_have_unread_notifications": "You have unread notifications.", + "new_notification": "ข้อความเตือนใหม่", + "you_have_unread_notifications": "คุณมีคำเตือนที่ยังไม่ได้อ่าน", "new_message_from": "New message from %1", "upvoted_your_post_in": "%1 has upvoted your post in %2.", "moved_your_post": "%1 has moved your post.", @@ -20,8 +20,8 @@ "user_posted_topic": "%1 has posted a new topic: %2", "user_mentioned_you_in": "%1 mentioned you in %2", "user_started_following_you": "%1 started following you.", - "email-confirmed": "Email Confirmed", - "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", - "email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.", - "email-confirm-sent": "Confirmation email sent." + "email-confirmed": "Email ได้รับการยืนยันแล้ว", + "email-confirmed-message": "ขอบคุณที่ยืนยัน Email ของคุณ บัญชีของคุณสามารถใช้งานได้แล้ว", + "email-confirm-error-message": "มีปัญหาในการยืนยัน Email ของคุณ บางทีรหัสไม่ถูกต้องหรือหมดอายุแล้ว", + "email-confirm-sent": "Email เพื่อยืนยันได้ส่งไปแล้ว" } \ No newline at end of file diff --git a/public/language/th/pages.json b/public/language/th/pages.json index 91fe3f989a..2f253c17a6 100644 --- a/public/language/th/pages.json +++ b/public/language/th/pages.json @@ -5,7 +5,8 @@ "recent": "กระทู้ล่าสุด", "users": "ผู้ใช้ที่ลงทะเบียน", "notifications": "แจ้งเตือน", - "tags": "หัวข้อที่ถูก Tag อยู่ภายใต้ \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "แก้ไข \"%1\"", "user.following": "ผู้ใช้ที่ %1 ติดตาม", "user.followers": "ผู้ใช้ที่ติดตาม %1", diff --git a/public/language/th/reset_password.json b/public/language/th/reset_password.json index 1b66d7dd33..b2a61b08d7 100644 --- a/public/language/th/reset_password.json +++ b/public/language/th/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "รหัสรีเซ็ตถูกส่งออกไปแล้ว", "invalid_email": "อีเมล์ไม่ถูกต้อง / อีเมล์ไม่มีอยู่!", "password_too_short": "รหัสผ่านที่คุณกำหนดยังสั้นเกินไป กรุณากำหนดรหัสผ่านของคุณใหม่", - "passwords_do_not_match": "รหัสผ่านทั้ง 2 ที่ใส่ไม่ตรงกัน" + "passwords_do_not_match": "รหัสผ่านทั้ง 2 ที่ใส่ไม่ตรงกัน", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/th/search.json b/public/language/th/search.json index 167d50d0de..4590d8d648 100644 --- a/public/language/th/search.json +++ b/public/language/th/search.json @@ -1,40 +1,40 @@ { "results_matching": "%1 ผลลัพธ์ ตรงตามที่ระบุ \"%2\", (%3 วินาที)", - "no-matches": "No matches found", + "no-matches": "ไม่พบผลลัพธ์ที่สอดคล้อง", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", + "titles": "หัวข้อ", + "titles-posts": "หัวข้อ และ ข้อความ", + "posted-by": "บันทึกโดย", "in-categories": "In Categories", "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", + "reply-count": "จำนวนข้อความตอบกลับ", + "at-least": "อย่างน้อยที่สุด", + "at-most": "อย่างมากที่สุด", "post-time": "Post time", "newer-than": "Newer than", "older-than": "Older than", "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", + "yesterday": "เมื่อวาน", + "one-week": "1 สัปดาห์", + "two-weeks": "2 สัปดาห์", + "one-month": "1 เดือน", + "three-months": "3 เดือน", + "six-months": "6 เดือน", + "one-year": "1 ปี", + "sort-by": "จัดเรียงโดย", + "last-reply-time": "เวลาตอบกลับล่าสุด", "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", + "number-of-replies": "จำนวนข้อความตอบกลับ", + "number-of-views": "จำนวนดู", "topic-start-date": "Topic start date", "username": "Username", "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order", + "descending": "เรียงจากมากไปน้อย", + "ascending": "เรียงจากน้อยไปมาก", "save-preferences": "Save preferences", "clear-preferences": "Clear preferences", "search-preferences-saved": "Search preferences saved", "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "show-results-as": "แสดงผลลัพธ์แบบ" } \ No newline at end of file diff --git a/public/language/th/user.json b/public/language/th/user.json index 5fc906103c..f53b390dd7 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -27,6 +27,7 @@ "chat": "แชท", "follow": "ติดตาม", "unfollow": "เลิกติดตาม", + "more": "More", "profile_update_success": "ข้อมูลประวัติส่วนตัวได้รับการแก้ไขแล้ว", "change_picture": "เปลี่ยนรูป", "edit": "แก้ไข", @@ -47,7 +48,6 @@ "upload_picture": "อัปโหลดรูป", "upload_a_picture": "อัปโหลดรูป", "image_spec": "คุณสามารถอัปโหลด PNG, JPG หรือ GIF ไฟล์เท่านั้น", - "max": "สูงสุด", "settings": "ตั้งค่า", "show_email": "แสดงอีเมล์", "show_fullname": "แสดงชื่อจริง", @@ -68,15 +68,16 @@ "has_no_watched_topics": "ผู้ใช้นี้ยังไม่เคยเข้าชมในหัวข้อใดๆ", "email_hidden": "ซ่อนอีเมล์", "hidden": "ซ่อน", - "paginate_description": "ให้เลขหน้ากระทู้และโพสต์แทนการใช้สกรอลล์ที่ไม่สิ้นสุด", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "จำนวนกระทู้ต่อหน้า", "posts_per_page": "จำนวนโพสต์ต่อหน้า", - "notification_sounds": "เตือนด้วยเสียงเมื่อมีข้อความแจ้งเตือน", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "เปิดดูการตั้งค่า", - "open_links_in_new_tab": "เปิดลิงค์ในแท็บใหม่", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "เปิดใช้การค้นหาแบบ In-Topic", - "topic_search_help": "เมื่อการค้นหาแบบ In-Topic ถูกเปิดใช้งาน การค้นหาแบบ In-Topic จะทำงานแทนการค้นหาในรูปแบบเดิม ซึ่งช่วยให้คุณสามารถทำการค้นหาจาก Topic ทั้งหมด เพิ่มเติมจากที่คุณกำลังเห็นอยู่บนหน้าจอ", - "follow_topics_you_reply_to": "ติดตามกระทู้ที่คุณตอบ", - "follow_topics_you_create": "ติดตามกระทู้ที่คุณตั้ง", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/tr/category.json b/public/language/tr/category.json index 518bd9dcb0..b78064fb51 100644 --- a/public/language/tr/category.json +++ b/public/language/tr/category.json @@ -5,5 +5,8 @@ "browsing": "gözden geçiriliyor", "no_replies": "Kimse yanıtlamadı", "share_this_category": "Bu kategoriyi paylaş", - "ignore": "Yoksay" + "watch": "İzle", + "ignore": "Yoksay", + "watch.message": "Şuan bu kategorideki güncellemeleri izliyorsunuz", + "ignore.message": "Şuan bu kategoriden güncellemeleri gizliyorsunuz" } \ No newline at end of file diff --git a/public/language/tr/error.json b/public/language/tr/error.json index 475b93ac94..49e7247239 100644 --- a/public/language/tr/error.json +++ b/public/language/tr/error.json @@ -18,13 +18,14 @@ "username-taken": "Kullanıcı İsmi Alınmış", "email-taken": "E-posta Alınmış", "email-not-confirmed": "E-postanız onaylanmamış, onaylamak için lütfen buraya tıklayın.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "E-postanız onaylanana kadar sohbet edemezsiniz, onaylamak için lütfen buraya tıklayın.", "no-email-to-confirm": "Bu forum e-posta doğrulaması gerektirir, lütfen buraya bir e-posta adresi girin", "email-confirm-failed": "E-posta adresinizi doğrulayamıyoruz. Lütfen daha sonra tekrar deneyin.", + "confirm-email-already-sent": "E-mail onayı zaten gönderilmiş, yeni bir onay göndermek için lütfen 1 dakika bekleyin.", "username-too-short": "Kullanıcı ismi çok kısa", "username-too-long": "Kullanıcı ismi çok uzun.", "user-banned": "Kullanıcı Yasaklı", - "user-too-new": "Özür dileriz, ilk iletinizi yapmadan önce %1 saniye beklemeniz gerekiyor", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Kategori Yok", "no-topic": "Başlık Yok", "no-post": "İleti Yok", @@ -35,17 +36,17 @@ "no-emailers-configured": "E-posta eklentisi kurulu değil bu yüzden test e-postası gönderilemedi", "category-disabled": "Kategori aktif değil", "topic-locked": "Başlık Kilitli", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Lütfen yüklemelerin bitmesini bekleyin.", - "content-too-short": "Lütfen daha uzun bir ileti girin. En az %1 karakter.", - "content-too-long": "Lütfen daha kısa bir yayın girin. Yayınlar %1 karakterden uzun olamaz.", - "title-too-short": "Lütfen daha uzun bir başlık girin. En az %1 karakter.", - "title-too-long": "Lütfen daha kısa bir başlık girin. Başlıklar %1 karakterden uzun olamaz.", - "too-many-posts": "Sadece %1 saniyede bir ileti gönderebilirsiniz.", - "too-many-posts-newbie": "Yeni bir kullanıcı olarak, %2 saygınlığınız olana kadar sadece %1 saniyede bir mesaj gönderebilirsiniz. Lütfen tekrar ileti göndermeden önce bekleyin.", - "tag-too-short": "Lütfen daha uzun bir etiket giriniz. Etiketler en az %1 karakter uzunluğunda olmalı", - "tag-too-long": "Lütfen daha kısa bir etiket girin. Etiketler %1 karakterden daha uzun olamaz", - "file-too-big": "İzin verilen en büyük dosya boyutu %1 kbs.", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Kendi iletinize oy veremezsiniz", "already-favourited": "Bu iletiyi zaten favorilerinize eklediniz", "already-unfavourited": "Bu iletiyi zaten favorilerinizden çıkardınız", @@ -62,10 +63,11 @@ "post-already-restored": "İleti zaten geri getirilmiş", "topic-already-deleted": "Başlık zaten silinmiş", "topic-already-restored": "Başlık zaten geri getirilmiş", + "cant-purge-main-post": "İlk iletiyi silemezsiniz, bunun yerine konuyu silin", "topic-thumbnails-are-disabled": "Başlık resimleri kapalı.", "invalid-file": "Geçersiz Dosya", "uploads-are-disabled": "Yüklemeler kapalı", - "signature-too-long": "İmza en fazla %1 karakter olabilir!", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Kendinizle sohbet edemezsiniz!", "chat-restricted": "Bu kullanıcı sohbet ayarlarını kısıtlamış. Bu kişiye mesaj gönderebilmeniz için sizi takip etmeleri gerekiyor", "too-many-messages": "Ardı ardına çok fazla mesaj yolladınız, lütfen biraz bekleyiniz.", diff --git a/public/language/tr/groups.json b/public/language/tr/groups.json index 536921e7e5..4c92ecd5d1 100644 --- a/public/language/tr/groups.json +++ b/public/language/tr/groups.json @@ -16,10 +16,12 @@ "details.has_no_posts": "Bu grubun üyeleri henüz bir ileti göndermedi.", "details.latest_posts": "En son iletiler", "details.private": "Özel", - "details.grant": "Grant/Rescind Ownership", + "details.grant": "Gurup sahibi yap/yapma", "details.kick": "Dışarı at", "details.owner_options": "Grup Yöneticisi", "details.group_name": "Grup ismi", + "details.member_count": "Üye Sayısı", + "details.creation_date": "Oluşturulma Tarihi", "details.description": "Tanımlama", "details.badge_preview": "Rozet Önizlemesi", "details.change_icon": "İkonu Değiştir", diff --git a/public/language/tr/modules.json b/public/language/tr/modules.json index af25aa3d20..47929205ca 100644 --- a/public/language/tr/modules.json +++ b/public/language/tr/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 Gün", "chat.thirty_days": "30 Gün", "chat.three_months": "3 Ay", + "composer.compose": "Yaz", + "composer.show_preview": "Önizleme Göster", + "composer.hide_preview": "Önizleme Sakla", "composer.user_said_in": "%1 %2 içinde söyledi:", "composer.user_said": "%1 söyledi:", "composer.discard": "Bu iletiyi iptal etmek istediğinizden eminmisiniz?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Gönder ve Kitle", + "composer.toggle_dropdown": "Menü aç" } \ No newline at end of file diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 84f3ef0148..922562f348 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -5,7 +5,8 @@ "recent": "Güncel Konular", "users": "Kayıtlı Kullanıcılar", "notifications": "Bildirimler", - "tags": "“%1“ ile etiketlenmiş konular", + "tags": "Etiketler", + "tag": "“%1“ ile etiketlenmiş konular", "user.edit": "\"% 1\" düzenleniyor", "user.following": "İnsanlar %1 Takip Ediyor", "user.followers": "%1 takip edenler", @@ -14,7 +15,7 @@ "user.groups": "%1 Kişisine Ait Gruplar", "user.favourites": "%1'in Favori İletileri", "user.settings": "Kullanıcı Ayarları", - "user.watched": "Topics watched by %1", + "user.watched": "%1 tarafından izlenen konular", "maintenance.text": "%1 şu anda bakımda. Lütfen bir süre sonra tekrar deneyin.", "maintenance.messageIntro": "Ayrıca, yönetici şu mesaji bıraktı:" } \ No newline at end of file diff --git a/public/language/tr/reset_password.json b/public/language/tr/reset_password.json index 60f0346516..eb1eabab39 100644 --- a/public/language/tr/reset_password.json +++ b/public/language/tr/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Şifre Yenilemesi Gönderildi", "invalid_email": "Geçersiz E-posta / E-posta mevcut değil!", "password_too_short": "Girdiğiniz şifre çok kısa, lütfen farklı bir şifre seçiniz.", - "passwords_do_not_match": "Girdiğiniz iki şifre birbirine uymuyor." + "passwords_do_not_match": "Girdiğiniz iki şifre birbirine uymuyor.", + "password_expired": "Parolanız sona erdi, lütfen yeni bir parola seçin" } \ No newline at end of file diff --git a/public/language/tr/search.json b/public/language/tr/search.json index 2ffbbfe36e..9b3d86185c 100644 --- a/public/language/tr/search.json +++ b/public/language/tr/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 tane “%2“ bulundu (%3 saniye)", "no-matches": "Hiç eşleşme bulunamadı", + "advanced-search": "Gelişmiş Arama", "in": "Konum:", - "by": "Kim Tarafından :", "titles": "Başlıklar", "titles-posts": "Başlıklar ve Yayınlar", "posted-by": "Gönderen", diff --git a/public/language/tr/tags.json b/public/language/tr/tags.json index 9f614ce38c..344a4bfff6 100644 --- a/public/language/tr/tags.json +++ b/public/language/tr/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "Bu etiketli başlık yok.", "tags": "Etiketler", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "Etiketleri buraya girin. %1-%2 karakter. Her etiketten sonra enter tuşuna basın.", "enter_tags_here_short": "Etiketleri gir...", "no_tags": "Henüz etiket yok." } \ No newline at end of file diff --git a/public/language/tr/user.json b/public/language/tr/user.json index cfca7130fb..285cb3ebc5 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -27,6 +27,7 @@ "chat": "Sohbet", "follow": "Takip Et", "unfollow": "Takip etme", + "more": "Daha Fazla", "profile_update_success": "Profiliniz başarıyla güncellendi!", "change_picture": "Resmi Değiştir", "edit": "Düzenle", @@ -47,7 +48,6 @@ "upload_picture": "Resim Yükle", "upload_a_picture": "Bir Resim Yükle", "image_spec": "Sadece PNG, JPG veya GIF dosyaları yükleyebilirsiniz", - "max": "maks.", "settings": "Ayarlar", "show_email": "E-postamı göster", "show_fullname": "Tam ismimi göster", @@ -60,7 +60,7 @@ "digest_monthly": "Aylık", "send_chat_notifications": "Çevrimiçi değilken gelen iletileri e-posta olarak gönder", "send_post_notifications": "Abone olduğum konulara cevap gelince bana eposta yolla", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "Bazı ayar değişiklikleri sayfayı tekrar yüklemenizi gerektirir. Buraya tıklayarak sayfayı tekrar yükleyebilirsiniz.", "has_no_follower": "Bu kullanıcının hiç takipçisi yok :(", "follows_no_one": "Bu kullanıcı kimseyi takip etmiyor :(", "has_no_posts": "Bu kullanıcı henüz birşey göndermedi.", @@ -68,15 +68,16 @@ "has_no_watched_topics": "Bu kullanıcı henüz hiçbir konuyu izlemedi.", "email_hidden": "E-posta gizli", "hidden": "gizli", - "paginate_description": "Sonsuz yükleme yerine konu ve iletileri sayfalara böl.", + "paginate_description": "Sonsuz yükleme yerine konu ve iletileri sayfalara böl", "topics_per_page": "Sayfa başına Konular", "posts_per_page": "Sayfa başına İletiler", - "notification_sounds": "Bildirim alındığında ses çal", + "notification_sounds": "Uyarı alındığında ses çal", "browsing": "Tarayıcı Ayaları", - "open_links_in_new_tab": "Dışarı giden bağlantıları yeni sekmede aç?", + "open_links_in_new_tab": "Dışarı giden bağlantıları yeni sekmede aç", "enable_topic_searching": "Konu içi aramayı aktive et", - "topic_search_help": "Aktive edilirse, konu içi arama tarayıcının normal arama davranışını değiştirerek tüm konuyu aramanızı sağlar.", - "follow_topics_you_reply_to": "İleti gönderdiğim konuları takip et.", - "follow_topics_you_create": "Kendi yarattığım konuları takip et.", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "Aktive edilirse, konu içi arama tarayıcının normal arama davranışını değiştirerek tüm konuyu aramanızı sağlar", + "follow_topics_you_reply_to": "Cevap verdiğim konuları takip et", + "follow_topics_you_create": "Kendi konularımı takip et", + "grouptitle": "Göstermek istediğiniz gurup başlığını seçin", + "no-group-title": "Grup başlığı yok" } \ No newline at end of file diff --git a/public/language/vi/category.json b/public/language/vi/category.json index be80ee1583..e209512922 100644 --- a/public/language/vi/category.json +++ b/public/language/vi/category.json @@ -5,5 +5,8 @@ "browsing": "đang xem", "no_replies": "Chưa có bình luận nào", "share_this_category": "Chia sẻ thư mục này", - "ignore": "Bỏ qua" + "watch": "Watch", + "ignore": "Bỏ qua", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 5a9d22c3ee..3aecae1d63 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "Tên đăng nhập quá ngắn", "username-too-long": "Tên đăng nhập quá dài", "user-banned": "Tài khoản bị ban", - "user-too-new": "Xin thứ lỗi, bạn cần đợi %1 giây trước khi tạo bài viết đầu tiên", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "Danh mục không tồn tại", "no-topic": "Chủ đề không tồn tại", "no-post": "Bài viết không tồn tại", @@ -35,17 +36,17 @@ "no-emailers-configured": "Không có trình cắm email nào được tải, vì vậy email kiểm tra không thể gửi được", "category-disabled": "Danh mục bị khóa", "topic-locked": "Chủ đề bị khóa", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "Vui lòng chờ upload", - "content-too-short": "Vui lòng tạo bài viết dài hơn. Bài viết phải có ít nhất %1 ký tự.", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "Vui lòng nhập tiêu đề dài hơn. Tiêu đề phải có ít nhất %1 ký tự", - "title-too-long": "Yêu cầu tiêu đề ngắn hơn. Không dài quá %1 ký tự", - "too-many-posts": "Bạn chỉ có thể gửi một bài viết mỗi %1 giây - Xin chờ trong giây lát trước khi gửi lại.", - "too-many-posts-newbie": "Tài khoản mới chỉ có thể gởi 1 bài mỗi %1 giây cho đến khi bạn có được %2 điểm tín nhiệm - Xin vui lòng chờ giây lát trước khi thử lại", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "Kích thước tối đa cho phép là %1 kb - xin lựa chọn tập tin nhỏ hơn", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "Bạn không thể vote cho chính bài viết của bạn", "already-favourited": "Bạn đã bấm yêu thích cho bài viết này rồi", "already-unfavourited": "Bạn đã bỏ thích bài này rồi", @@ -62,10 +63,11 @@ "post-already-restored": "Bài viết này đã được phục hồi", "topic-already-deleted": "Chủ đề này đã bị xóa", "topic-already-restored": "Chủ đề này đã được phục hồi", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "Thumbnails cho chủ đề đã bị tắt", "invalid-file": "File không hợp lệ", "uploads-are-disabled": "Đã khóa lựa chọn tải lên", - "signature-too-long": "Chứ ký không được dài quá %1 ký tự", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "Bạn không thể chat với chính bạn!", "chat-restricted": "Người dùng này đã bật chế độ hạn chế tin nhắn chat. Bạn phải được anh/cô ta follow thì mới có thể gởi tin nhắn đến họ được.", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/vi/global.json b/public/language/vi/global.json index a7dc6ce134..a1a8420d6e 100644 --- a/public/language/vi/global.json +++ b/public/language/vi/global.json @@ -26,8 +26,8 @@ "header.unread": "Chưa đọc", "header.tags": "Tags", "header.popular": "Nổi bật", - "header.users": "Số người dùng", - "header.groups": "Groups", + "header.users": "Thành viên", + "header.groups": "Nhóm", "header.chats": "Phần Chat", "header.notifications": "Thông báo", "header.search": "Tìm kiếm", @@ -75,7 +75,7 @@ "updated.title": "Cập nhật diễn đàn", "updated.message": "Diễn đàn đã được cập nhật bản mới nhất. Click vào đây để tải lại trang.", "privacy": "Quyền riêng tư", - "follow": "Follow", - "unfollow": "Unfollow", + "follow": "Theo dõi", + "unfollow": "Huỷ theo dõi", "delete_all": "Xóa hết" } \ No newline at end of file diff --git a/public/language/vi/groups.json b/public/language/vi/groups.json index 511c942133..8122f449cd 100644 --- a/public/language/vi/groups.json +++ b/public/language/vi/groups.json @@ -1,15 +1,15 @@ { "groups": "Nhóm", "view_group": "Xem nhóm", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", + "owner": "Trưởng nhóm", + "new_group": "Tạo nhóm mới", + "no_groups_found": "Không có nhóm nào để hiển thị", + "pending.accept": "Chấp nhận", + "pending.reject": "Từ chối", "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", + "cover-change": "Thay đổi", + "cover-save": "Lưu", + "cover-saving": "Đang lưu", "details.title": "Thông tin nhóm", "details.members": "Danh sách thành viên", "details.pending": "Pending Members", @@ -19,7 +19,9 @@ "details.grant": "Grant/Rescind Ownership", "details.kick": "Kick", "details.owner_options": "Group Administration", - "details.group_name": "Group Name", + "details.group_name": "Tên nhóm", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", @@ -29,6 +31,6 @@ "details.private_help": "If enabled, joining of groups requires approval from a group owner", "details.hidden": "Hidden", "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", + "event.updated": "Thông tin nhóm đã được cập nhật", "event.deleted": "The group \"%1\" has been deleted" } \ No newline at end of file diff --git a/public/language/vi/modules.json b/public/language/vi/modules.json index ecf53d1f0f..2d54413f69 100644 --- a/public/language/vi/modules.json +++ b/public/language/vi/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7 ngày", "chat.thirty_days": "30 ngày", "chat.three_months": "3 tháng", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1 đã nói trong %2:", "composer.user_said": "%1 đã nói:", "composer.discard": "Bạn có chắc chắn hủy bỏ bài viết này?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/vi/pages.json b/public/language/vi/pages.json index 717907a267..aa2afcd916 100644 --- a/public/language/vi/pages.json +++ b/public/language/vi/pages.json @@ -5,7 +5,8 @@ "recent": "Chủ đề gần đây", "users": "Số người dùng đã đăng ký", "notifications": "Thông báo", - "tags": "Chủ đề được tag theo \"%1\"", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "Chỉnh sửa \"%1\"", "user.following": "Người mà %1 theo dõi", "user.followers": "Người đang theo dõi %1", diff --git a/public/language/vi/recent.json b/public/language/vi/recent.json index b97df0e137..12a131279d 100644 --- a/public/language/vi/recent.json +++ b/public/language/vi/recent.json @@ -7,13 +7,13 @@ "alltime": "Tất cả thời gian", "no_recent_topics": "Không có chủ đề nào gần đây", "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", + "there-is-a-new-topic": "Có chủ đề mới", + "there-is-a-new-topic-and-a-new-post": "Có chủ đề mới và bài viết mới", "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", "there-are-new-topics": "There are %1 new topics.", "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", + "there-is-a-new-post": "Có bài viết mới", "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "click-here-to-reload": "Nhấn vào đây để tải lại" } \ No newline at end of file diff --git a/public/language/vi/reset_password.json b/public/language/vi/reset_password.json index 32db56aab3..7e3ce09565 100644 --- a/public/language/vi/reset_password.json +++ b/public/language/vi/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "Đã gửi mật khẩu được thiết lập lại", "invalid_email": "Email không đúng / Email không tồn tại!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/vi/search.json b/public/language/vi/search.json index c9c176ba74..312c315c73 100644 --- a/public/language/vi/search.json +++ b/public/language/vi/search.json @@ -1,8 +1,8 @@ { "results_matching": "%1 kết quả(s) trùng với \"%2\", (%3 giây)", "no-matches": "No matches found", + "advanced-search": "Advanced Search", "in": "In", - "by": "By", "titles": "Titles", "titles-posts": "Titles and Posts", "posted-by": "Posted by", diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index 8c9f8cdfce..9d0ca5e82d 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -12,7 +12,7 @@ "notify_me": "Được thông báo khi có trả lời mới trong chủ đề này", "quote": "Trích dẫn", "reply": "Trả lời", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Hãy đăng nhập để trả lời", "edit": "Chỉnh sửa", "delete": "Xóa", "purge": "Xóa hẳn", @@ -33,7 +33,7 @@ "not_following_topic.message": "Bạn sẽ không còn nhận được thông báo từ chủ đề này", "login_to_subscribe": "Xin hãy đăng ký hoặc đăng nhập để theo dõi topic này", "markAsUnreadForAll.success": "Chủ đề đã được đánh dấu là chưa đọc toàn bộ", - "watch": "Xem", + "watch": "Theo dõi", "unwatch": "Ngừng theo dõi", "watch.title": "Được thông báo khi có trả lời mới trong chủ đề này", "unwatch.title": "Ngừng theo dõi chủ đề này", @@ -75,7 +75,7 @@ "fork_no_pids": "Chưa chọn bài gửi nào!", "fork_success": "Tạo bản sao thành công! Nhấn vào đây để chuyển tới chủ đề vừa tạo.", "composer.title_placeholder": "Nhập tiêu đề cho chủ đề của bạn tại đây...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Tên", "composer.discard": "Loại bỏ", "composer.submit": "Gửi", "composer.replying_to": "Đang trả lời %1", @@ -95,5 +95,5 @@ "oldest_to_newest": "Cũ đến mới", "newest_to_oldest": "Mới đến cũ", "most_votes": "Bình chọn nhiều nhất", - "most_posts": "Most posts" + "most_posts": "Có nhiều bài viết nhất" } \ No newline at end of file diff --git a/public/language/vi/user.json b/public/language/vi/user.json index 64fa5b6cdd..d567c55285 100644 --- a/public/language/vi/user.json +++ b/public/language/vi/user.json @@ -2,8 +2,8 @@ "banned": "Bị cấm", "offline": "Offline", "username": "Tên truy cập", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Ngày gia nhập", + "postcount": "Số bài viết", "email": "Email", "confirm_email": "Xác nhận email", "delete_account": "Xóa tài khoản", @@ -15,10 +15,10 @@ "joined": "Đã gia nhập", "lastonline": "Online lần cuối vào", "profile": "Hồ sơ", - "profile_views": "Khung hiển thị hồ sơ", + "profile_views": "Số lượt người ghé thăm", "reputation": "Mức uy tín", "favourites": "Yêu thích", - "watched": "Watched", + "watched": "Đã theo dõi", "followers": "Số người theo dõi", "following": "Đang theo dõi", "signature": "Chữ ký", @@ -27,6 +27,7 @@ "chat": "Chat", "follow": "Theo dõi", "unfollow": "Hủy theo dõi", + "more": "Xem thêm", "profile_update_success": "Hồ sơ đã được cập nhật thành công", "change_picture": "Thay đổi hình ảnh", "edit": "Chỉnh sửa", @@ -47,7 +48,6 @@ "upload_picture": "Tải lên hình ảnh", "upload_a_picture": "Tải lên một hình ảnh", "image_spec": "Bạn chỉ có thể tải lên được các file PNG, JPG hoặc GIF", - "max": "tối đa", "settings": "Thiết lập", "show_email": "Hiện Email của tôi", "show_fullname": "Hiện tên đầy đủ", @@ -59,24 +59,25 @@ "digest_weekly": "Hàng tuần", "digest_monthly": "Hàng tháng", "send_chat_notifications": "Gửi một email nếu có tin nhắn chat mới đến và tôi không online", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "send_post_notifications": "Gửi email khi có trả lời mới trong chủ đề mà tôi subscribe", + "settings-require-reload": "Một số thay đổi trong cài đặt đòi hỏi tải lại. Nhấn vào đây để tải lại trang.", "has_no_follower": "Người dùng này hiện chưa có ai theo dõi :(", "follows_no_one": "Người dùng này hiện chưa theo dõi ai :(", "has_no_posts": "Người dùng này chưa viết bài nào", "has_no_topics": "Người dùng này chưa tạo một chủ đề nào", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "Thành viên này chưa theo dõi chủ đề nào. ", "email_hidden": "Ẩn Email", "hidden": "Đã ẩn", - "paginate_description": "Phân trang cho chủ đề và bài viết thay vì cuộn liên tục", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "Số chủ đề trong một trang", "posts_per_page": "Số bài viết trong một trang", - "notification_sounds": "Xuất hiện âm thanh khi bạn nhận được một thông báo", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "Đang xem cài đặt", - "open_links_in_new_tab": "Mở liên kết trong tab mới?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Bật In-topic Searching", - "topic_search_help": "Nếu được bật, in-topic searching sẽ thay thế tính năng tìm kiếu mặc định của trình duyệt và giúp bạn tìm trong toàn bộ nội dung bài viết, thay vì chỉ tìm trong những gì đang có trên màn hình.", - "follow_topics_you_reply_to": "Theo dõi chủ đề mà bạn trả lời", - "follow_topics_you_create": "Theo dõi chủ đề bạn tạo", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Chọn tên nhóm mà bạn muốn hiển thị", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/language/zh_CN/category.json b/public/language/zh_CN/category.json index 1d5d4fb2a5..14f4a36667 100644 --- a/public/language/zh_CN/category.json +++ b/public/language/zh_CN/category.json @@ -5,5 +5,8 @@ "browsing": "正在浏览", "no_replies": "尚无回复", "share_this_category": "分享此版块", - "ignore": "忽略" + "watch": "订阅", + "ignore": "忽略", + "watch.message": "您现在已经订阅了此版块", + "ignore.message": "您现在已经取消订阅了此版块" } \ No newline at end of file diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json index 0488353541..934a7e3991 100644 --- a/public/language/zh_CN/error.json +++ b/public/language/zh_CN/error.json @@ -18,13 +18,14 @@ "username-taken": "用户名已被占用", "email-taken": "电子邮箱已被占用", "email-not-confirmed": "您的电子邮箱尚未确认,请点击这里确认您的电子邮箱。", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", + "email-not-confirmed-chat": "您的电子邮箱尚未确认,无法聊天,请点击这里确认您的电子邮箱。", "no-email-to-confirm": "本论坛需要电子邮箱确认,请点击这里输入一个电子邮箱地址", "email-confirm-failed": "我们无法确认您的电子邮箱,请重试", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "用户名太短", "username-too-long": "用户名太长", "user-banned": "用户已禁止", - "user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "版面不存在", "no-topic": "主题不存在", "no-post": "帖子不存在", @@ -35,17 +36,17 @@ "no-emailers-configured": "未加载任何电子邮箱插件,无法发送测试邮件", "category-disabled": "版块已禁用", "topic-locked": "主题已锁定", - "post-edit-duration-expired": "您只能在发表后 %1 秒内修改内容", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "请等待上传完成", - "content-too-short": "请再输入一些内容,帖子至少要有 %1 个字符。", - "content-too-long": "请输入更短的发帖。发帖字数不能超过 %1 个字符。", - "title-too-short": "请再输入一些内容,标题至少要有 %1 个字符。", - "title-too-long": "请输入更短的标题。不超过 %1 字。", - "too-many-posts": "发帖间隔至少要 %1 秒 - 请稍候再发帖", - "too-many-posts-newbie": "作为新用户,您必须每隔 %1 秒才能发帖一次,直到您有 %2 点威望为止 —— 请稍候再发帖", - "tag-too-short": "标签长度过短,标签长度至少为 %1 个字符", - "tag-too-long": "标签长度过长,标签长度至多为 %1 个字符", - "file-too-big": "文件不能超过 %1k 字节 - 请上传更小的文件", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "您不能给自己的帖子投票。", "already-favourited": "您已收藏该帖", "already-unfavourited": "您已取消收藏此帖", @@ -62,10 +63,11 @@ "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 个字符。", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "您不能和自己聊天!", "chat-restricted": "此用户限制了他的聊天消息。必须他先关注您,您才能和他聊天。", "too-many-messages": "您发送了太多消息,请稍等片刻。", diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json index b7a60f7813..821707c77f 100644 --- a/public/language/zh_CN/groups.json +++ b/public/language/zh_CN/groups.json @@ -20,6 +20,8 @@ "details.kick": "踢", "details.owner_options": "用户组管理", "details.group_name": "用户组名", + "details.member_count": "会员总数", + "details.creation_date": "创建时间", "details.description": "描述", "details.badge_preview": "标志预览", "details.change_icon": "更改图标", diff --git a/public/language/zh_CN/modules.json b/public/language/zh_CN/modules.json index e309d83bbc..e6a2678ea4 100644 --- a/public/language/zh_CN/modules.json +++ b/public/language/zh_CN/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7天", "chat.thirty_days": "30天", "chat.three_months": "3个月", + "composer.compose": "编写", + "composer.show_preview": "显示预览", + "composer.hide_preview": "隐藏预览", "composer.user_said_in": "%1 在 %2 中说:", "composer.user_said": "%1 说:", "composer.discard": "确定想要取消此帖?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "提交并锁定", + "composer.toggle_dropdown": "标为 Dropdown" } \ No newline at end of file diff --git a/public/language/zh_CN/pages.json b/public/language/zh_CN/pages.json index 298f183f74..1ec319a476 100644 --- a/public/language/zh_CN/pages.json +++ b/public/language/zh_CN/pages.json @@ -5,7 +5,8 @@ "recent": "最新主题", "users": "已注册用户", "notifications": "提醒", - "tags": "话题为 \"%1\" 的主题", + "tags": "话题", + "tag": "\"%1\" 下的主题", "user.edit": "正在编辑 \"%1\"", "user.following": "%1 关注", "user.followers": "关注 %1 的人", @@ -14,7 +15,7 @@ "user.groups": "%1 的用户组", "user.favourites": "%1 收藏的帖子", "user.settings": "用户设置", - "user.watched": "Topics watched by %1", + "user.watched": "主题已被 %1 关注", "maintenance.text": "%1 正在进行维护。请稍后再来。", "maintenance.messageIntro": "此外,管理员留下的消息:" } \ No newline at end of file diff --git a/public/language/zh_CN/reset_password.json b/public/language/zh_CN/reset_password.json index 9b0ca29dbe..5ade84926c 100644 --- a/public/language/zh_CN/reset_password.json +++ b/public/language/zh_CN/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "密码重置邮件已发送。", "invalid_email": "无效的电子邮箱/电子邮箱不存在!", "password_too_short": "密码太短,请选择其他密码。", - "passwords_do_not_match": "您输入两个密码不一致。" + "passwords_do_not_match": "您输入两个密码不一致。", + "password_expired": "您的密码已过期,请选择新密码" } \ No newline at end of file diff --git a/public/language/zh_CN/search.json b/public/language/zh_CN/search.json index 9df978a688..f24707dbc8 100644 --- a/public/language/zh_CN/search.json +++ b/public/language/zh_CN/search.json @@ -1,8 +1,8 @@ { "results_matching": "共 %1 条结果匹配 \"%2\",(耗时 %3 秒)", "no-matches": "无匹配结果", + "advanced-search": "高级搜索", "in": "在", - "by": "-", "titles": "标题", "titles-posts": "标题和回帖", "posted-by": "发表", diff --git a/public/language/zh_CN/tags.json b/public/language/zh_CN/tags.json index 9802f9c79a..9ffbfbfb82 100644 --- a/public/language/zh_CN/tags.json +++ b/public/language/zh_CN/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "此话题还没有主题帖。", "tags": "话题", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "在这里输入话题,每个话题 %1 到 %2 个字符。", "enter_tags_here_short": "输入话题...", "no_tags": "尚无话题。" } \ No newline at end of file diff --git a/public/language/zh_CN/user.json b/public/language/zh_CN/user.json index 6d29052981..583c03fd09 100644 --- a/public/language/zh_CN/user.json +++ b/public/language/zh_CN/user.json @@ -27,6 +27,7 @@ "chat": "聊天", "follow": "关注", "unfollow": "取消关注", + "more": "更多", "profile_update_success": "资料已经成功更新。", "change_picture": "更改头像", "edit": "编辑", @@ -47,7 +48,6 @@ "upload_picture": "上传头像", "upload_a_picture": "上传头像", "image_spec": "您只能上传 PNG, JPG 或者 GIF 图片文件", - "max": "最大", "settings": "设置", "show_email": "显示我的电子邮箱", "show_fullname": "显示我的全名", @@ -60,7 +60,7 @@ "digest_monthly": "每月", "send_chat_notifications": "当我不在线,并受到新的聊天消息时给我发邮件", "send_post_notifications": "我订阅的主题有回复时发送邮件", - "settings-require-reload": "Some setting changes require a reload. Click here to reload the page.", + "settings-require-reload": "一些设置变更需要刷新页面。点击这里刷新页面。", "has_no_follower": "此用户还没有粉丝 :(", "follows_no_one": "此用户尚未关注任何人 :(", "has_no_posts": "此用户尚未发布任何帖子。", @@ -68,15 +68,16 @@ "has_no_watched_topics": "此用户还未订阅任何主题", "email_hidden": "电子邮箱已隐藏", "hidden": "隐藏", - "paginate_description": "分页展示主题和帖子,替代滚动展示。", + "paginate_description": "使用分页式版块浏览", "topics_per_page": "每页主题数", "posts_per_page": "每页帖子数", - "notification_sounds": "收到通知时播放提示音。", + "notification_sounds": "收到通知时播放提示音", "browsing": "浏览设置", - "open_links_in_new_tab": "在新标签中打开外部链接?", + "open_links_in_new_tab": "在新标签打开外部链接", "enable_topic_searching": "启用主题内搜索", - "topic_search_help": "启用后,主题内搜索会替代浏览器默认的页面搜索,你可以在整个主题的全部内容进行搜索,而不是仅限于屏幕显示的内容。", - "follow_topics_you_reply_to": "关注您回复的主题。", - "follow_topics_you_create": "关注您创建的主题。", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "如果启用,主题内搜索会替代浏览器默认的网页搜索,使你可以在整个主题内搜索,而不仅仅时页面上展现的内容。", + "follow_topics_you_reply_to": "关注你回复的主题", + "follow_topics_you_create": "关注你创建的主题", + "grouptitle": "选择展现的组内称号", + "no-group-title": "无组内头衔" } \ No newline at end of file diff --git a/public/language/zh_TW/category.json b/public/language/zh_TW/category.json index 055d4a70d3..b018f21e89 100644 --- a/public/language/zh_TW/category.json +++ b/public/language/zh_TW/category.json @@ -5,5 +5,8 @@ "browsing": "正在瀏覽", "no_replies": "還沒有回覆", "share_this_category": "分享這類別", - "ignore": "忽略" + "watch": "Watch", + "ignore": "忽略", + "watch.message": "You are now watching updates from this category", + "ignore.message": "You are now ignoring updates from this category" } \ No newline at end of file diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index 3aae7dd5bc..5fa681cee7 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -21,10 +21,11 @@ "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", "email-confirm-failed": "We could not confirm your email, please try again later.", + "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", "username-too-short": "用戶名太短", "username-too-long": "用戶名太長", "user-banned": "該使用者已被停用", - "user-too-new": "對不起,您要先等%1秒鐘才能發佈第一篇文章", + "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "no-category": "類別並不存在", "no-topic": "主題並不存在", "no-post": "文章並不存在", @@ -35,17 +36,17 @@ "no-emailers-configured": "未加載電郵插件,所以無法發送測試郵件", "category-disabled": "該類別已被關閉", "topic-locked": "該主題已被鎖定", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting", "still-uploading": "請等待上傳完成。", - "content-too-short": "請輸入一個較長的帖子。 帖子需至少有 %1 個字。", - "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", - "title-too-short": "請輸入一個較長的標題。 標題需至少有 %1 個字。", - "title-too-long": "請輸入一個較短的主題名稱。 標題不能超過 %1 個字元。", - "too-many-posts": "你必須間隔 %1 秒後才能發表文章-請稍後", - "too-many-posts-newbie": "新用戶在贏得%2信譽前,每隔%1秒才能發佈新文章-請稍後再試", - "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 characters", - "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 characters", - "file-too-big": "允許的最大檔案大小是 %1 kbs-請上傳一個較小的檔案", + "content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).", + "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).", + "title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).", + "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).", + "too-many-posts": "You can only post once every %1 second(s) - please wait before posting again", + "too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", + "tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", + "tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", + "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", "cant-vote-self-post": "你不能對自己的文章說讚!", "already-favourited": "你已經收藏了這篇文章", "already-unfavourited": "你已放棄收藏這篇文章", @@ -62,10 +63,11 @@ "post-already-restored": "此文章已還原", "topic-already-deleted": "此主題已經被刪除", "topic-already-restored": "此主題已還原", + "cant-purge-main-post": "You can't purge the main post, please delete the topic instead", "topic-thumbnails-are-disabled": "禁用主題縮圖", "invalid-file": "無效的檔案", "uploads-are-disabled": "上傳功能被停用", - "signature-too-long": "對不起,簽名檔長度不能超過 %1 字元!", + "signature-too-long": "Sorry, your signature cannot be longer than %1 character(s).", "cant-chat-with-yourself": "你不能與自己聊天!", "chat-restricted": "此用戶已限制了他的聊天功能。你要在他關注你之後,才能跟他聊天", "too-many-messages": "You have sent too many messages, please wait awhile.", diff --git a/public/language/zh_TW/groups.json b/public/language/zh_TW/groups.json index 97401e5d7d..caaab4b118 100644 --- a/public/language/zh_TW/groups.json +++ b/public/language/zh_TW/groups.json @@ -20,6 +20,8 @@ "details.kick": "Kick", "details.owner_options": "Group Administration", "details.group_name": "Group Name", + "details.member_count": "Member Count", + "details.creation_date": "Creation Date", "details.description": "Description", "details.badge_preview": "Badge Preview", "details.change_icon": "Change Icon", diff --git a/public/language/zh_TW/modules.json b/public/language/zh_TW/modules.json index 3e7d758914..7e39342ebf 100644 --- a/public/language/zh_TW/modules.json +++ b/public/language/zh_TW/modules.json @@ -15,8 +15,12 @@ "chat.seven_days": "7日", "chat.thirty_days": "30日", "chat.three_months": "3個月", + "composer.compose": "Compose", + "composer.show_preview": "Show Preview", + "composer.hide_preview": "Hide Preview", "composer.user_said_in": "%1在%2裡說:", "composer.user_said": "%1說:", "composer.discard": "你確定要放棄這帖子嗎?", - "composer.submit_and_lock": "Submit and Lock" + "composer.submit_and_lock": "Submit and Lock", + "composer.toggle_dropdown": "Toggle Dropdown" } \ No newline at end of file diff --git a/public/language/zh_TW/pages.json b/public/language/zh_TW/pages.json index 20af148f85..43825b4004 100644 --- a/public/language/zh_TW/pages.json +++ b/public/language/zh_TW/pages.json @@ -5,7 +5,8 @@ "recent": "近期的主題", "users": "已註冊的使用者", "notifications": "新訊息通知", - "tags": "符合\"%1\"標籤的主題", + "tags": "Tags", + "tag": "Topics tagged under \"%1\"", "user.edit": "編輯中 \"%1\"", "user.following": "People %1 Follows", "user.followers": "People who Follow %1", diff --git a/public/language/zh_TW/reset_password.json b/public/language/zh_TW/reset_password.json index 9f0ab67aa1..af7cbce277 100644 --- a/public/language/zh_TW/reset_password.json +++ b/public/language/zh_TW/reset_password.json @@ -12,5 +12,6 @@ "password_reset_sent": "密碼重設郵件已發送。", "invalid_email": "非法的郵箱地址/郵箱不存在!", "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "passwords_do_not_match": "The two passwords you've entered do not match.", + "password_expired": "Your password has expired, please choose a new password" } \ No newline at end of file diff --git a/public/language/zh_TW/search.json b/public/language/zh_TW/search.json index eef48e983e..9eadd12f6d 100644 --- a/public/language/zh_TW/search.json +++ b/public/language/zh_TW/search.json @@ -1,8 +1,8 @@ { "results_matching": "有%1個跟\"%2\"相符的結果(%3秒)", "no-matches": "沒有找到相符的主題", + "advanced-search": "Advanced Search", "in": "在", - "by": "由", "titles": "標題", "titles-posts": "標題與發布", "posted-by": "Posted by", diff --git a/public/language/zh_TW/user.json b/public/language/zh_TW/user.json index 008808ae6f..a177bfb876 100644 --- a/public/language/zh_TW/user.json +++ b/public/language/zh_TW/user.json @@ -27,6 +27,7 @@ "chat": "聊天", "follow": "關注", "unfollow": "取消關注", + "more": "More", "profile_update_success": "您的個人資料已更新成功!", "change_picture": "改變頭像", "edit": "編輯", @@ -47,7 +48,6 @@ "upload_picture": "上傳頭像", "upload_a_picture": "上傳一張照片", "image_spec": "支援的圖檔格式包含 PNG, JPG, 或是 GIF", - "max": "max.", "settings": "設定", "show_email": "顯示我的郵箱", "show_fullname": "顯示我的全名", @@ -68,15 +68,16 @@ "has_no_watched_topics": "這位使用者尚未發表任何主題", "email_hidden": "郵箱被隱藏", "hidden": "隱藏", - "paginate_description": "使用分頁取代瀏覽載入文章模式.", + "paginate_description": "Paginate topics and posts instead of using infinite scroll", "topics_per_page": "每頁的主題數", "posts_per_page": "每頁的文章數", - "notification_sounds": "當收到新消息時播放提示音", + "notification_sounds": "Play a sound when you receive a notification", "browsing": "瀏覽設定", - "open_links_in_new_tab": "在新的分頁開啟外部連結?", + "open_links_in_new_tab": "Open outgoing links in new tab", "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "關注您回复的主題。", - "follow_topics_you_create": "關注您創建的主題。", - "grouptitle": "Select the group title you would like to display" + "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen", + "follow_topics_you_reply_to": "Follow topics that you reply to", + "follow_topics_you_create": "Follow topics you create", + "grouptitle": "Select the group title you would like to display", + "no-group-title": "No group title" } \ No newline at end of file diff --git a/public/less/admin/general/dashboard.less b/public/less/admin/general/dashboard.less index 71c2298d31..cf44ceb176 100644 --- a/public/less/admin/general/dashboard.less +++ b/public/less/admin/general/dashboard.less @@ -102,10 +102,6 @@ border-color: #949FB1; background-color: #A8B3C5; } - &.tags { - border-color: #FDB45C; - background-color: #8FA633; - } } } } @@ -130,7 +126,7 @@ } .monthly-pageviews { - width:50%; + width:33%; } .motd textarea { diff --git a/public/less/install.less b/public/less/install.less new file mode 100644 index 0000000000..1289c62941 --- /dev/null +++ b/public/less/install.less @@ -0,0 +1,54 @@ +.btn, .form-control, .navbar { + border-radius: 0; +} + +.container { + font-size: 18px; + margin-bottom: 100px; +} + +body, small, p, div { + font-family: "Roboto", sans-serif; +} + +.input-row { + margin-bottom: 20px; + + .form-control { + margin-bottom: 5px; + } + + .help-text { + pointer-events: none; + line-height: 20px; + color: #888; + font-size: 85%; + display: none; + } + + .input-field { + border-right: 5px solid #FFF; + } + + &.active { + .input-field { + border-right-color: #38B44A; + padding-right: 20px; + } + + .help-text { + display: block; + } + } + + &.error { + .input-field { + border-right-color: #BF3E11; + padding-right: 20px; + } + + .help-text { + display: block; + } + } +} \ No newline at end of file diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index 73201a2db6..d44dc1d980 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -19,6 +19,7 @@ selectMenuItem(data.url); setupHeaderMenu(); + setupRestartLinks(); }); $(window).resize(setupHeaderMenu); @@ -114,6 +115,8 @@ } function selectMenuItem(url) { + url = url.replace(/\/\d+$/, ''); + $('#main-menu .nav-list > li').removeClass('active').each(function() { var menu = $(this), category = menu.parents('.sidebar-nav'), @@ -148,4 +151,61 @@ $('.mobile-header').remove(); } } + + function setupRestartLinks() { + $('.restart').off('click').on('click', function() { + bootbox.confirm('Are you sure you wish to restart NodeBB?', function(confirm) { + if (confirm) { + app.alert({ + alert_id: 'instance_restart', + type: 'info', + title: 'Restarting... ', + message: 'NodeBB is restarting.', + timeout: 5000 + }); + + $(window).one('action:reconnected', function() { + app.alert({ + alert_id: 'instance_restart', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully restarted.', + timeout: 5000 + }); + }); + + socket.emit('admin.restart'); + } + }); + }); + + $('.reload').off('click').on('click', function() { + app.alert({ + alert_id: 'instance_reload', + type: 'info', + title: 'Reloading... ', + message: 'NodeBB is reloading.', + timeout: 5000 + }); + + socket.emit('admin.reload', function(err) { + if (!err) { + app.alert({ + alert_id: 'instance_reload', + type: 'success', + title: ' Success', + message: 'NodeBB has successfully reloaded.', + timeout: 5000 + }); + } else { + app.alert({ + alert_id: 'instance_reload', + type: 'danger', + title: '[[global:alert.error]]', + message: '[[error:reload-failed, ' + err.message + ']]' + }); + } + }); + }); + } }()); \ No newline at end of file diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js index a842c0d2af..6b7cdd5c28 100644 --- a/public/src/admin/general/dashboard.js +++ b/public/src/admin/general/dashboard.js @@ -31,9 +31,7 @@ define('admin/general/dashboard', ['semver'], function(semver) { usedTopicColors.length = 0; }); - $('#logout-link').on('click', function() { - app.logout(); - }); + $('[component="logout"]').on('click', app.logout); $.get('https://api.github.com/repos/NodeBB/NodeBB/tags', function(releases) { // Re-sort the releases, as they do not follow Semver (wrt pre-releases) @@ -61,61 +59,6 @@ define('admin/general/dashboard', ['semver'], function(semver) { } }); - $('.restart').on('click', function() { - bootbox.confirm('Are you sure you wish to restart NodeBB?', function(confirm) { - if (confirm) { - app.alert({ - alert_id: 'instance_restart', - type: 'info', - title: 'Restarting... ', - message: 'NodeBB is restarting.', - timeout: 5000 - }); - - $(window).one('action:reconnected', function() { - app.alert({ - alert_id: 'instance_restart', - type: 'success', - title: ' Success', - message: 'NodeBB has successfully restarted.', - timeout: 5000 - }); - }); - - socket.emit('admin.restart'); - } - }); - }); - - $('.reload').on('click', function() { - app.alert({ - alert_id: 'instance_reload', - type: 'info', - title: 'Reloading... ', - message: 'NodeBB is reloading.', - timeout: 5000 - }); - - socket.emit('admin.reload', function(err) { - if (!err) { - app.alert({ - alert_id: 'instance_reload', - type: 'success', - title: ' Success', - message: 'NodeBB has successfully reloaded.', - timeout: 5000 - }); - } else { - app.alert({ - alert_id: 'instance_reload', - type: 'danger', - title: '[[global:alert.error]]', - message: '[[error:reload-failed, ' + err.message + ']]' - }); - } - }); - }); - setupGraphs(); }; @@ -283,13 +226,8 @@ define('admin/general/dashboard', ['semver'], function(semver) { color: "#949FB1", highlight: "#A8B3C5", label: "Recent/Unread" - }, - { - value: 1, - color: "#8FA633", - highlight: "#3FA7B8", - label: "Tags" - }], { + } + ], { responsive: true }); @@ -338,8 +276,10 @@ define('admin/general/dashboard', ['semver'], function(semver) { $('#pageViewsThisMonth').html(data.monthlyPageViews.thisMonth); $('#pageViewsLastMonth').html(data.monthlyPageViews.lastMonth); + $('#pageViewsPastDay').html(data.pastDay); utils.addCommasToNumbers($('#pageViewsThisMonth')); utils.addCommasToNumbers($('#pageViewsLastMonth')); + utils.addCommasToNumbers($('#pageViewsPastDay')); }); } @@ -354,7 +294,6 @@ define('admin/general/dashboard', ['semver'], function(semver) { graphs.presence.segments[1].value = users.topics; graphs.presence.segments[2].value = users.category; graphs.presence.segments[3].value = users.recent; - graphs.presence.segments[4].value = users.tags; graphs.presence.update(); } diff --git a/public/src/admin/general/languages.js b/public/src/admin/general/languages.js index 8720f96df2..24d6a418c9 100644 --- a/public/src/admin/general/languages.js +++ b/public/src/admin/general/languages.js @@ -2,7 +2,11 @@ /*global define*/ define('admin/general/languages', ['admin/settings'], function(Settings) { - $(function() { + var Languages = {} + + Languages.init = function() { Settings.prepare(); - }); + }; + + return Languages; }); diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index c51dba4a01..238317144c 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -203,7 +203,9 @@ define('admin/manage/category', [ } else { app.alertError('[[error:invalid-data]]'); } - }) + }); + + Category.exposeAssumedPrivileges(); }; Category.refreshPrivilegeTable = function() { @@ -216,10 +218,33 @@ define('admin/manage/category', [ privileges: privileges }, function(html) { $('.privilege-table-container').html(html); + Category.exposeAssumedPrivileges(); }); }); }; + Category.exposeAssumedPrivileges = function() { + /* + If registered-users has a privilege enabled, then all users and groups of that privilege + should be assumed to have that privilege as well, even if not set in the db, so reflect + this arrangement in the table + */ + var privs = []; + $('.privilege-table tr[data-group-name="registered-users"] td input[type="checkbox"]').parent().each(function(idx, el) { + if ($(el).find('input').prop('checked')) { + privs.push(el.getAttribute('data-privilege')); + } + }); + for(var x=0,numPrivs=privs.length;x parseInt(numRecentReplies, 10)) { + if (category.find('[component="category/posts"]').length > parseInt(numRecentReplies, 10)) { recentPosts.last().remove(); } diff --git a/public/src/client/category.js b/public/src/client/category.js index c74141feec..777d3f808a 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -47,7 +47,7 @@ define('forum/category', [ navigator.init('[component="category/topic"]', ajaxify.variables.get('topic_count'), Category.toTop, Category.toBottom, Category.navigatorCallback); } - $('[component="category"]').on('click', '[component="post/header"]', function() { + $('[component="category"]').on('click', '[component="topic/header"]', function() { var clickedIndex = $(this).parents('[data-index]').attr('data-index'); $('[component="category/topic"]').each(function(index, el) { if ($(el).offset().top - $(window).scrollTop() > 0) { @@ -73,6 +73,8 @@ define('forum/category', [ $('.watch').toggleClass('hidden', command === 'watch'); $('.ignore').toggleClass('hidden', command === 'ignore'); + + app.alertSuccess('[[category:' + command + '.message]]') }); }); } @@ -92,7 +94,7 @@ define('forum/category', [ }; $(window).on('action:popstate', function(ev, data) { - if (data.url.indexOf('category/') === 0) { + if (data.url.startsWith('category/')) { var cid = data.url.match(/^category\/(\d+)/); if (cid && cid[1]) { cid = cid[1]; @@ -184,8 +186,11 @@ define('forum/category', [ $(window).trigger('filter:categories.new_topic', topic); + var editable = !!$('.thread-tools').length; + templates.parse('category', 'topics', { - privileges: {editable: !!$('.thread-tools').length}, + privileges: {editable: editable}, + showSelect: editable, topics: [topic] }, function(html) { translator.translate(html, function(translatedHTML) { @@ -276,6 +281,8 @@ define('forum/category', [ return; } + data.showSelect = data.privileges.editable; + findInsertionPoint(); templates.parse('category', 'topics', data, function(html) { @@ -335,17 +342,16 @@ define('forum/category', [ after: after, author: utils.params().author }, function (data, done) { - if (data.topics && data.topics.length) { Category.onTopicsLoaded(data, function() { done(); callback(); }); - $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); } + $('[component="category"]').attr('data-nextstart', data.nextStart); $(window).trigger('action:categories.loaded'); }); } diff --git a/public/src/client/categoryTools.js b/public/src/client/categoryTools.js index d5e69d1312..348ff684f8 100644 --- a/public/src/client/categoryTools.js +++ b/public/src/client/categoryTools.js @@ -13,38 +13,58 @@ define('forum/categoryTools', ['forum/topic/move', 'topicSelect', 'components', topicSelect.init(updateDropdownOptions); - $('.delete_thread').on('click', function(e) { - var tids = topicSelect.getSelectedTids(); - categoryCommand(isAny(isTopicDeleted, tids) ? 'restore' : 'delete', tids); + components.get('topic/delete').on('click', function() { + categoryCommand('delete', topicSelect.getSelectedTids()); return false; }); - $('.purge_thread').on('click', function() { + components.get('topic/restore').on('click', function() { + categoryCommand('restore', topicSelect.getSelectedTids()); + return false; + }); + + components.get('topic/purge').on('click', function() { categoryCommand('purge', topicSelect.getSelectedTids()); return false; }); - $('.lock_thread').on('click', function() { + components.get('topic/lock').on('click', function() { + var tids = topicSelect.getSelectedTids(); + if (tids.length) { + socket.emit('topics.lock', {tids: tids, cid: CategoryTools.cid}, onCommandComplete); + } + return false; + }); + + components.get('topic/unlock').on('click', function() { var tids = topicSelect.getSelectedTids(); if (tids.length) { - socket.emit(isAny(isTopicLocked, tids) ? 'topics.unlock' : 'topics.lock', {tids: tids, cid: CategoryTools.cid}, onCommandComplete); + socket.emit('topics.unlock', {tids: tids, cid: CategoryTools.cid}, onCommandComplete); } return false; }); - $('.pin_thread').on('click', function() { + components.get('topic/pin').on('click', function() { var tids = topicSelect.getSelectedTids(); if (tids.length) { - socket.emit(isAny(isTopicPinned, tids) ? 'topics.unpin' : 'topics.pin', {tids: tids, cid: CategoryTools.cid}, onCommandComplete); + socket.emit('topics.pin', {tids: tids, cid: CategoryTools.cid}, onCommandComplete); } return false; }); - $('.markAsUnreadForAll').on('click', function() { + components.get('topic/unpin').on('click', function() { + var tids = topicSelect.getSelectedTids(); + if (tids.length) { + socket.emit('topics.unpin', {tids: tids, cid: CategoryTools.cid}, onCommandComplete); + } + return false; + }); + + components.get('topic/mark-unread-for-all').on('click', function() { var tids = topicSelect.getSelectedTids(); if (tids.length) { socket.emit('topics.markAsUnreadForAll', tids, function(err) { - if(err) { + if (err) { return app.alertError(err.message); } app.alertSuccess('[[topic:markAsUnreadForAll.success]]'); @@ -56,7 +76,7 @@ define('forum/categoryTools', ['forum/topic/move', 'topicSelect', 'components', return false; }); - $('.move_thread').on('click', function() { + components.get('topic/move').on('click', function() { var tids = topicSelect.getSelectedTids(); if (tids.length) { @@ -65,7 +85,7 @@ define('forum/categoryTools', ['forum/topic/move', 'topicSelect', 'components', return false; }); - $('.move_all_threads').on('click', function() { + components.get('topic/move-all').on('click', function() { move.init(null, cid, function(err) { ajaxify.refresh(); }); @@ -130,16 +150,22 @@ define('forum/categoryTools', ['forum/topic/move', 'topicSelect', 'components', } function updateDropdownOptions() { + var tids = topicSelect.getSelectedTids(); var isAnyDeleted = isAny(isTopicDeleted, tids); var areAllDeleted = areAll(isTopicDeleted, tids); var isAnyPinned = isAny(isTopicPinned, tids); var isAnyLocked = isAny(isTopicLocked, tids); - $('.delete_thread span').translateHtml(' [[topic:thread_tools.' + (isAnyDeleted ? 'restore' : 'delete') + ']]'); - $('.pin_thread').translateHtml(' [[topic:thread_tools.' + (isAnyPinned ? 'unpin' : 'pin') + ']]'); - $('.lock_thread').translateHtml(' [[topic:thread_tools.' + (isAnyLocked ? 'un': '') + 'lock]]'); - $('.purge_thread').toggleClass('hidden', !areAllDeleted); + components.get('topic/delete').toggleClass('hidden', isAnyDeleted); + components.get('topic/restore').toggleClass('hidden', !isAnyDeleted); + components.get('topic/purge').toggleClass('hidden', !areAllDeleted); + + components.get('topic/lock').toggleClass('hidden', isAnyLocked); + components.get('topic/unlock').toggleClass('hidden', !isAnyLocked); + + components.get('topic/pin').toggleClass('hidden', isAnyPinned); + components.get('topic/unpin').toggleClass('hidden', !isAnyPinned); } function isAny(method, tids) { diff --git a/public/src/client/chats.js b/public/src/client/chats.js index d506d81654..95fa9266b7 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -2,7 +2,7 @@ /* globals define, app, ajaxify, utils, socket, templates */ -define('forum/chats', ['string', 'sounds', 'forum/infinitescroll', 'translator'], function(S, sounds, infinitescroll, translator) { +define('forum/chats', ['components', 'string', 'sounds', 'forum/infinitescroll', 'translator'], function(components, S, sounds, infinitescroll, translator) { var Chats = { initialised: false }; @@ -69,9 +69,14 @@ define('forum/chats', ['string', 'sounds', 'forum/infinitescroll', 'translator'] uid = Chats.getRecipientUid(); if (app.previousUrl && app.previousUrl.match(/chats/)) { + var text = components.get('chat/input').val(); ajaxify.go('chats', function() { app.openChat(username, uid); }, true); + + $(window).one('action:chat.loaded', function() { + components.get('chat/input').val(text); + }); } else { window.history.go(-1); } @@ -163,16 +168,7 @@ define('forum/chats', ['string', 'sounds', 'forum/infinitescroll', 'translator'] }); socket.on('event:user_status_change', function(data) { - var userEl = $('.chats-list li[data-uid="' + data.uid +'"]'); - - if (userEl.length) { - var statusEl = userEl.find('.status'); - translator.translate('[[global:' + data.status + ']]', function(translated) { - statusEl.attr('class', 'fa fa-circle status ' + data.status) - .attr('title', translated) - .attr('data-original-title', translated); - }); - } + app.updateUserStatus($('.chats-list [data-uid="' + data.uid + '"] [component="user/status"]'), data.status); }); }; diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index bc1534f051..1a2742fb6b 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -1,13 +1,13 @@ "use strict"; /* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH, utils */ -define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect) { +define('forum/groups/details', ['iconSelect', 'components', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect, components) { var Details = { cover: {} }; Details.init = function() { - var detailsPage = $('.groups'), + var detailsPage = components.get('groups/container'), settingsFormEl = detailsPage.find('form'); if (ajaxify.variables.get('is_owner') === 'true') { @@ -15,7 +15,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', Details.initialiseCover(); } - $('.latest-posts .content img').addClass('img-responsive'); + components.get('groups/activity').find('.content img').addClass('img-responsive'); detailsPage.on('click', '[data-action]', function() { var btnEl = $(this), @@ -82,7 +82,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', }; Details.prepareSettings = function() { - var settingsFormEl = $('.groups form'), + var settingsFormEl = components.get('groups/settings'), colorBtn = settingsFormEl.find('[data-action="color-select"]'), colorValueEl = settingsFormEl.find('[name="labelColor"]'), iconBtn = settingsFormEl.find('[data-action="icon-select"]'), @@ -120,7 +120,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', // Disable user title customisation options if the the user title itself is disabled userTitleEnabledEl.on('change', function() { - var customOpts = $('.user-title-option input, .user-title-option button'); + var customOpts = components.get('groups/userTitleOption'); if (this.checked) { customOpts.removeAttr('disabled'); @@ -133,7 +133,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', }; Details.update = function() { - var settingsFormEl = $('.groups form'), + var settingsFormEl = components.get('groups/settings'), checkboxes = settingsFormEl.find('input[type="checkbox"][name]'); if (settingsFormEl.length) { @@ -194,11 +194,12 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', // Cover Photo Handling Code Details.initialiseCover = function() { - var coverEl = $('.group-cover'); + var coverEl = components.get('groups/cover'); coverEl.find('.change').on('click', function() { coverEl.toggleClass('active', 1); coverEl.backgroundDraggable({ - axis: 'y' + axis: 'y', + units: 'percent' }); coverEl.on('dragover', Details.cover.onDragOver); coverEl.on('drop', Details.cover.onDrop); @@ -213,7 +214,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', groupName: ajaxify.variables.get('group_name') }, function(err, data) { if (!err) { - var coverEl = $('.group-cover'); + var coverEl = components.get('groups/cover'); if (data['cover:url']) { coverEl.css('background-image', 'url(' + data['cover:url'] + ')'); } @@ -236,7 +237,7 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', }; Details.cover.onDrop = function(e) { - var coverEl = $('.group-cover'); + var coverEl = components.get('groups/cover'); e.stopPropagation(); e.preventDefault(); @@ -246,7 +247,6 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', if (files.length && files[0].type.match('image.*')) { reader.onload = function(e) { coverEl.css('background-image', 'url(' + e.target.result + ')'); - coverEl.backgroundDraggable(); Details.cover.newCover = e.target.result; }; @@ -255,14 +255,14 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', }; Details.cover.save = function() { - var coverEl = $('.group-cover'); + var coverEl = components.get('groups/cover'); coverEl.addClass('saving'); socket.emit('groups.cover.update', { groupName: ajaxify.variables.get('group_name'), imageData: Details.cover.newCover || undefined, - position: $('.group-cover').css('background-position') + position: components.get('groups/cover').css('background-position') }, function(err) { if (!err) { coverEl.toggleClass('active', 0); diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index 2fe5caab95..42bdbf88ae 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -36,13 +36,11 @@ define('forum/groups/list', function() { $('#search-sort').on('change', Groups.search); }; - Groups.search = function(event) { + Groups.search = function() { var groupsEl = $('#groups-list'), queryEl = $('#search-text'), sortEl = $('#search-sort'); - event.preventDefault(); - socket.emit('groups.search', { query: queryEl.val(), options: { @@ -57,6 +55,7 @@ define('forum/groups/list', function() { groupsEl.empty().append(html); }); }); + return false; }; return Groups; diff --git a/public/src/client/login.js b/public/src/client/login.js index 3032e3b35d..34fde568c8 100644 --- a/public/src/client/login.js +++ b/public/src/client/login.js @@ -13,37 +13,36 @@ define('forum/login', ['csrf', 'translator'], function(csrf, translator) { e.preventDefault(); if (!$('#username').val() || !$('#password').val()) { - translator.translate('[[error:invalid-username-or-password]]', function(translated) { - errorEl.find('p').text(translated); - errorEl.show(); - }); + errorEl.find('p').translateText('[[error:invalid-username-or-password]]'); + errorEl.show(); } else { errorEl.hide(); - if (!submitEl.hasClass('disabled')) { - submitEl.addClass('disabled'); - formEl.ajaxSubmit({ - headers: { - 'x-csrf-token': csrf.get() - }, - success: function(data, status) { - window.location.href = data; - }, - error: function(data, status) { - translator.translate(data.responseText, config.defaultLang, function(translated) { - errorEl.find('p').text(translated); - errorEl.show(); - submitEl.removeClass('disabled'); - }); - } - }); + if (submitEl.hasClass('disabled')) { + return; } + + submitEl.addClass('disabled'); + formEl.ajaxSubmit({ + headers: { + 'x-csrf-token': csrf.get() + }, + success: function(data, status) { + window.location.href = data; + }, + error: function(data, status) { + errorEl.find('p').translateText(data.responseText); + errorEl.show(); + submitEl.removeClass('disabled'); + } + }); } }); $('#login-error-notify button').on('click', function(e) { e.preventDefault(); errorEl.hide(); + return false; }); $('#content #username').focus(); diff --git a/public/src/client/pagination.js b/public/src/client/pagination.js index e2df0a6ebe..a94967c7c5 100644 --- a/public/src/client/pagination.js +++ b/public/src/client/pagination.js @@ -11,7 +11,7 @@ define('forum/pagination', function() { pagination.currentPage = parseInt(currentPage, 10); pagination.pageCount = parseInt(pageCount, 10); - $('.pagination').on('click', '.select_page', function(e) { + $('.pagination').on('click', '.select-page', function(e) { e.preventDefault(); bootbox.prompt('Enter page number:', function(pageNum) { pagination.loadPage(pageNum); @@ -23,12 +23,10 @@ define('forum/pagination', function() { callback = callback || function() {}; page = parseInt(page, 10); if (!utils.isNumber(page) || page < 1 || page > pagination.pageCount) { - callback(false); - return false; + return; } var url = window.location.pathname.slice(1).split('/').slice(0, 3).join('/') + '?page=' + page; ajaxify.go(url, callback); - return true; }; return pagination; diff --git a/public/src/client/recent.js b/public/src/client/recent.js index 909cec052e..fc55ee9b08 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -2,7 +2,7 @@ /* globals define, app, socket, utils */ -define('forum/recent', ['forum/infinitescroll', 'composer', 'components'], function(infinitescroll, composer, components) { +define('forum/recent', ['forum/infinitescroll', 'components'], function(infinitescroll, components) { var Recent = {}; var newTopicCount = 0, @@ -93,10 +93,10 @@ define('forum/recent', ['forum/infinitescroll', 'composer', 'components'], funct }, function(data, done) { if (data.topics && data.topics.length) { Recent.onTopicsLoaded('recent', data.topics, false, done); - $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); } + $('[component="category"]').attr('data-nextstart', data.nextStart); }); }; diff --git a/public/src/client/register.js b/public/src/client/register.js index cab41bb72f..6955fcb989 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -24,8 +24,9 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { } }); + // Update the "others can mention you via" text username.on('keyup', function() { - $('#yourUsername').text(this.value.length > 0 ? this.value : 'username'); + $('#yourUsername').text(this.value.length > 0 ? utils.slugify(this.value) : 'username'); }); username.on('blur', function() { diff --git a/public/src/client/reset.js b/public/src/client/reset.js index 568d0739a1..fcf4098b68 100644 --- a/public/src/client/reset.js +++ b/public/src/client/reset.js @@ -11,7 +11,7 @@ define('forum/reset', function() { $('#reset').on('click', function() { if (inputEl.val() && inputEl.val().indexOf('@') !== -1) { - socket.emit('user.reset.send', inputEl.val(), function(err, data) { + socket.emit('user.reset.send', inputEl.val(), function(err) { if(err) { return app.alertError(err.message); } diff --git a/public/src/client/search.js b/public/src/client/search.js index 900a4d1d6d..027f76ca18 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -8,9 +8,9 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco Search.init = function() { var searchQuery = $('#results').attr('data-search-query'); - $('#advanced-search #search-input').val(searchQuery); + $('#search-input').val(searchQuery); - var searchIn = $('#advanced-search #search-in'); + var searchIn = $('#search-in'); fillOutForm(); @@ -23,7 +23,7 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco $('#advanced-search').off('submit').on('submit', function(e) { e.preventDefault(); - var input = $(this).find('#search-input'); + var input = $('#search-input'); var searchData = getSearchData(); searchData.term = input.val(); @@ -41,7 +41,7 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco function getSearchData() { var form = $('#advanced-search'); var searchData = { - in: form.find('#search-in').val() + in: $('#search-in').val() }; if (searchData.in === 'posts' || searchData.in === 'titlesposts' || searchData.in === 'titles') { @@ -117,14 +117,18 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco return; } - var regexStr = searchQuery.trim().split(' ').join('|'); - var regex = new RegExp('(' + regexStr + ')', 'gi'); + try { + var regexStr = searchQuery.trim().split(' ').join('|'); + var regex = new RegExp('(' + regexStr + ')', 'gi'); - $('.search-result-text').each(function() { - var result = $(this); - var text = result.html().replace(regex, '$1'); - result.html(text).find('img').addClass('img-responsive'); - }); + $('.search-result-text').each(function() { + var result = $(this); + var text = result.html().replace(regex, '$1'); + result.html(text).find('img').addClass('img-responsive'); + }); + } catch(e) { + return; + } } function handleSavePreferences() { diff --git a/public/src/client/tag.js b/public/src/client/tag.js index 7328934e2d..d2e6dd30b7 100644 --- a/public/src/client/tag.js +++ b/public/src/client/tag.js @@ -29,11 +29,11 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function(recent, i }, function(data, done) { if (data.topics && data.topics.length) { recent.onTopicsLoaded('tag', data.topics, false, done); - $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); $('#load-more-btn').hide(); } + $('[component="category"]').attr('data-nextstart', data.nextStart); }); } }; diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 3e087879d2..95b84204fe 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -30,12 +30,7 @@ define('forum/topic', [ }); Topic.init = function() { - var tid = ajaxify.variables.get('topic_id'), - thread_state = { - locked: ajaxify.variables.get('locked'), - deleted: ajaxify.variables.get('deleted'), - pinned: ajaxify.variables.get('pinned') - }; + var tid = ajaxify.variables.get('topic_id'); $(window).trigger('action:topic.loading'); @@ -43,8 +38,8 @@ define('forum/topic', [ posts.processPage($('.topic')); - postTools.init(tid, thread_state); - threadTools.init(tid, thread_state); + postTools.init(tid); + threadTools.init(tid); events.init(); sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.variables.get('topic_slug')); @@ -87,7 +82,8 @@ define('forum/topic', [ function handleBookmark(tid) { var bookmark = localStorage.getItem('topic:' + tid + ':bookmark'); var postIndex = getPostIndex(); - if (postIndex) { + + if (postIndex && window.location.search.indexOf('page=') === -1) { navigator.scrollToPost(postIndex - 1, true); } else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && ajaxify.variables.get('postcount') > 1) { app.alert({ diff --git a/public/src/client/topic/browsing.js b/public/src/client/topic/browsing.js index 5cb71960ea..2c125ba4ec 100644 --- a/public/src/client/topic/browsing.js +++ b/public/src/client/topic/browsing.js @@ -12,7 +12,7 @@ define('forum/topic/browsing', ['translator'], function(translator) { if (data && data.room.indexOf('topic_' + ajaxify.variables.get('topic_id')) !== -1) { $('[component="topic/browsing/list"]').parent().toggleClass('hidden', !data.users.length); for(var i=0; i'); + return $('

'); } } diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index f47108c7e1..d40fc776a1 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -1,7 +1,7 @@ 'use strict'; -/* globals app, ajaxify, define, socket, templates */ +/* globals config, app, ajaxify, define, socket, templates, translator, utils */ define('forum/topic/events', [ 'forum/topic/browsing', @@ -20,8 +20,8 @@ define('forum/topic/events', [ 'event:voted': updatePostVotesAndUserReputation, 'event:favourited': updateFavouriteCount, - 'event:topic_deleted': toggleTopicDeleteState, - 'event:topic_restored': toggleTopicDeleteState, + 'event:topic_deleted': threadTools.setDeleteState, + 'event:topic_restored': threadTools.setDeleteState, 'event:topic_purged': onTopicPurged, 'event:topic_locked': threadTools.setLockedState, @@ -81,11 +81,6 @@ define('forum/topic/events', [ $('[data-pid="' + data.post.pid + '"] .favouriteCount').html(data.post.reputation).attr('data-favourites', data.post.reputation); } - function toggleTopicDeleteState(data) { - threadTools.setLockedState(data); - threadTools.setDeleteState(data); - } - function onTopicPurged(data) { ajaxify.go('category/' + ajaxify.variables.get('category_id')); } @@ -97,17 +92,24 @@ define('forum/topic/events', [ } function onPostEdited(data) { - var editedPostEl = components.get('post/content', data.pid), - editedPostHeader = components.get('post/header', data.pid); + if (!data || !data.post) { + return; + } + var editedPostEl = components.get('post/content', data.post.pid), + editorEl = $('[data-pid="' + data.post.pid + '"] [component="post/editor"]'), + topicTitle = components.get('topic/title'); - if (editedPostHeader.length) { - editedPostHeader.fadeOut(250, function() { - editedPostHeader.html(data.title).fadeIn(250); + if (topicTitle.length && data.topic.title) { + var newUrl = 'topic/' + data.topic.slug + (window.location.search ? window.location.search : ''); + history.replaceState({url: newUrl}, null, window.location.protocol + '//' + window.location.host + config.relative_path + '/' + newUrl); + + topicTitle.fadeOut(250, function() { + topicTitle.html(data.topic.title).fadeIn(250); }); } editedPostEl.fadeOut(250, function() { - editedPostEl.html(data.content); + editedPostEl.html(data.post.content); editedPostEl.find('img').addClass('img-responsive'); app.replaceSelfLinks(editedPostEl.find('a')); editedPostEl.fadeIn(250); @@ -115,8 +117,21 @@ define('forum/topic/events', [ $(window).trigger('action:posts.edited', data); }); - if (data.tags && tagsUpdated(data.tags)) { - templates.parse('partials/post_bar', 'tags', {tags: data.tags}, function(html) { + var editData = { + editor: data.editor, + relativeEditTime: utils.toISOString(data.post.edited) + }; + + templates.parse('partials/topic/post-editor', editData, function(html) { + translator.translate(html, function(translated) { + html = $(translated); + editorEl.replaceWith(html); + html.find('.timeago').timeago(); + }); + }); + + if (data.topic.tags && tagsUpdated(data.topic.tags)) { + templates.parse('partials/post_bar', 'tags', {tags: data.topic.tags}, function(html) { var tags = $('.tags'); tags.fadeOut(250, function() { @@ -168,22 +183,16 @@ define('forum/topic/events', [ } function togglePostFavourite(data) { - var favBtn = $('[data-pid="' + data.post.pid + '"] .favourite'); + var favBtn = $('[data-pid="' + data.post.pid + '"] [component="post/favourite"]'); + if (!favBtn.length) { return; } - favBtn.addClass('btn-warning') - .attr('data-favourited', data.isFavourited); + favBtn.attr('data-favourited', data.isFavourited); - var icon = favBtn.find('i'); - var className = icon.attr('class'); - if (!className) { - return; - } - if (data.isFavourited ? className.indexOf('-o') !== -1 : className.indexOf('-o') === -1) { - icon.attr('class', data.isFavourited ? className.replace('-o', '') : className + '-o'); - } + favBtn.find('[component="post/favourite/on"]').toggleClass('hidden', !data.isFavourited); + favBtn.find('[component="post/favourite/off"]').toggleClass('hidden', data.isFavourited); } function togglePostVote(data) { @@ -192,7 +201,6 @@ define('forum/topic/events', [ post.find('[component="post/downvote"]').toggleClass('downvoted', data.downvote); } - function onNewNotification(data) { var tid = ajaxify.variables.get('topic_id'); if (data && data.tid && parseInt(data.tid, 10) === parseInt(tid, 10)) { diff --git a/public/src/client/topic/move.js b/public/src/client/topic/move.js index 21fcf999ff..648f9f3b27 100644 --- a/public/src/client/topic/move.js +++ b/public/src/client/topic/move.js @@ -78,7 +78,7 @@ define('forum/topic/move', function() { modal.modal('hide'); $('#move_thread_commit').prop('disabled', false); - if(err) { + if (err) { return app.alertError(err.message); } diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index b3686a7e3c..4a227252a8 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -2,15 +2,15 @@ /* globals define, app, ajaxify, bootbox, socket, templates, utils */ -define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', 'translator'], function(composer, share, navigator, components, translator) { +define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator'], function(share, navigator, components, translator) { var PostTools = {}, topicName; - PostTools.init = function(tid, threadState) { + PostTools.init = function(tid) { topicName = ajaxify.variables.get('topic_name'); - addPostHandlers(tid, threadState); + addPostHandlers(tid); share.addShareHandlers(topicName); @@ -23,15 +23,15 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', postEl.find('[component="post/quote"], [component="post/favourite"], [component="post/reply"], [component="post/flag"], [component="user/chat"]') .toggleClass('hidden', isDeleted); + postEl.find('[component="post/delete"]').toggleClass('hidden', isDeleted); + postEl.find('[component="post/restore"]').toggleClass('hidden', !isDeleted); postEl.find('[component="post/purge"]').toggleClass('hidden', !isDeleted); - postEl.find('[component="post/delete"] .i').toggleClass('fa-trash-o', !isDeleted).toggleClass('fa-history', isDeleted); - postEl.find('[component="post/delete"] span').translateHtml(isDeleted ? ' [[topic:restore]]' : ' [[topic:delete]]'); }; PostTools.updatePostCount = function() { socket.emit('topics.postcount', ajaxify.variables.get('topic_id'), function(err, postCount) { if (!err) { - var postCountEl = $('.topic-post-count'); + var postCountEl = components.get('topic/post-count'); postCountEl.html(postCount).attr('title', postCount); utils.makeNumbersHumanReadable(postCountEl); navigator.setCount(postCount); @@ -71,29 +71,19 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', } } - function addPostHandlers(tid, threadState) { - function canPost() { - return !threadState.locked || app.user.isAdmin; - } - + function addPostHandlers(tid) { var postContainer = components.get('topic'); postContainer.on('click', '[component="post/quote"]', function() { - if (canPost()) { - onQuoteClicked($(this), tid, topicName); - } + onQuoteClicked($(this), tid, topicName); }); postContainer.on('click', '[component="post/reply"]', function() { - if (canPost()) { - onReplyClicked($(this), tid, topicName); - } + onReplyClicked($(this), tid, topicName); }); components.get('topic/reply').on('click', function() { - if (canPost()) { - onReplyClicked($(this), tid, topicName); - } + onReplyClicked($(this), tid, topicName); }); postContainer.on('click', '[component="post/favourite"]', function() { @@ -117,11 +107,18 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', }); postContainer.on('click', '[component="post/edit"]', function(e) { - composer.editPost(getData($(this), 'data-pid')); + var btn = $(this); + require(['composer'], function(composer) { + composer.editPost(getData(btn, 'data-pid')); + }); }); postContainer.on('click', '[component="post/delete"]', function(e) { - deletePost($(this), tid); + togglePostDelete($(this), tid); + }); + + postContainer.on('click', '[component="post/restore"]', function(e) { + togglePostDelete($(this), tid); }); postContainer.on('click', '[component="post/purge"]', function(e) { @@ -132,52 +129,58 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', openMovePostModal($(this)); }); - postContainer.on('click', '[component="user/chat"]', function(e) { + postContainer.on('click', '[component="post/chat"]', function(e) { openChat($(this)); }); } function onReplyClicked(button, tid, topicName) { - var selectionText = '', - selection = window.getSelection ? window.getSelection() : document.selection.createRange(); - - if ($(selection.baseNode).parents('[component="post/content"]').length > 0) { - var snippet = selection.toString(); - if (snippet.length) { - selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n'; + require(['composer'], function(composer) { + var selectionText = '', + selection = window.getSelection ? window.getSelection() : document.selection.createRange(), + topicUUID = composer.findByTid(tid); + + if ($(selection.baseNode).parents('[component="post/content"]').length > 0) { + var snippet = selection.toString(); + if (snippet.length) { + selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n'; + } } - } - var username = getUserName(selectionText ? $(selection.baseNode) : button); - if (getData(button, 'data-uid') === '0') { - username = ''; - } - if (selectionText.length) { - composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), getData(button, 'data-pid'), topicName, username, selectionText); - } else { - composer.newReply(tid, getData(button, 'data-pid'), topicName, username ? username + ' ' : ''); - } + var username = getUserName(selectionText ? $(selection.baseNode) : button); + if (getData(button, 'data-uid') === '0') { + username = ''; + } + if (selectionText.length) { + composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), getData(button, 'data-pid'), topicName, username, selectionText, topicUUID); + } else { + composer.newReply(tid, getData(button, 'data-pid'), topicName, username ? username + ' ' : ''); + } + }); } function onQuoteClicked(button, tid, topicName) { - var username = getUserName(button), - pid = getData(button, 'data-pid'); - - socket.emit('posts.getRawPost', pid, function(err, post) { - if(err) { - return app.alertError(err.message); - } - var quoted = ''; - if(post) { - quoted = '> ' + post.replace(/\n/g, '\n> ') + '\n\n'; - } + require(['composer'], function(composer) { + var username = getUserName(button), + pid = getData(button, 'data-pid'), + topicUUID = composer.findByTid(tid); + + socket.emit('posts.getRawPost', pid, function(err, post) { + if(err) { + return app.alertError(err.message); + } + var quoted = ''; + if(post) { + quoted = '> ' + post.replace(/\n/g, '\n> ') + '\n\n'; + } - if($('.composer').length) { - composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), pid, topicName, username, quoted); - } else { - composer.newReply(tid, pid, topicName, '[[modules:composer.user_said, ' + username + ']]\n' + quoted); - } + if(topicUUID) { + composer.addQuote(tid, ajaxify.variables.get('topic_slug'), getData(button, 'data-index'), pid, topicName, username, quoted, topicUUID); + } else { + composer.newReply(tid, pid, topicName, '[[modules:composer.user_said, ' + username + ']]\n' + quoted); + } + }); }); } @@ -239,7 +242,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', function getUserName(button) { var username = '', - post = button.parents('li[data-pid]'); + post = button.parents('[data-pid]'); if (post.length) { username = post.attr('data-username').replace(/\s/g, '-'); @@ -251,7 +254,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', return username; } - function deletePost(button, tid) { + function togglePostDelete(button, tid) { var pid = getData(button, 'data-pid'), postEl = components.get('post', 'pid', pid), action = !postEl.hasClass('deleted') ? 'delete' : 'restore'; @@ -274,7 +277,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', pid: pid, tid: tid }, function(err) { - if(err) { + if (err) { app.alertError(err.message); } }); @@ -318,7 +321,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', socket.emit('topics.movePost', {pid: pid, tid: tid}, function(err) { $('#move-post-modal').addClass('hide'); - if(err) { + if (err) { $('#topicId').val(''); return app.alertError(err.message); } @@ -336,21 +339,22 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator', 'components', function flagPost(pid) { translator.translate('[[topic:flag_confirm]]', function(message) { bootbox.confirm(message, function(confirm) { - if (confirm) { - socket.emit('posts.flag', pid, function(err) { - if(err) { - return app.alertError(err.message); - } - - app.alertSuccess('[[topic:flag_success]]'); - }); + if (!confirm) { + return; } + socket.emit('posts.flag', pid, function(err) { + if (err) { + return app.alertError(err.message); + } + + app.alertSuccess('[[topic:flag_success]]'); + }); }); }); } function openChat(button) { - var post = button.parents('data-pid'); + var post = button.parents('[data-pid]'); app.openChat(post.attr('data-username'), post.attr('data-uid')); button.parents('.btn-group').find('.dropdown-toggle').click(); diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 1d7dcbe0df..2104bc04fa 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -253,7 +253,7 @@ define('forum/topic/posts', [ utils.addCommasToNumbers(element.find('.formatted-number')); utils.makeNumbersHumanReadable(element.find('.human-readable-number')); element.find('.timeago').timeago(); - element.find('[component="post/content"] img:not(.emoji)').addClass('img-responsive').each(function() { + element.find('[component="post/content"] img:not(.emoji)').each(function() { var $this = $(this); if (!$this.parent().is('a')) { $this.wrap(''); @@ -267,7 +267,7 @@ define('forum/topic/posts', [ function showBottomPostBar() { if(components.get('post').length > 1 || !components.get('post', 'index', 0).length) { - $('.bottom-post-bar').removeClass('hide'); + $('.bottom-post-bar').removeClass('hidden'); } } diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index 46a20d19b9..3bf8d539b2 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -6,23 +6,14 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp var ThreadTools = {}; - ThreadTools.init = function(tid, threadState) { - ThreadTools.threadState = threadState; - - if (threadState.locked) { - ThreadTools.setLockedState({tid: tid, isLocked: true}); - } - - if (threadState.deleted) { - ThreadTools.setDeleteState({tid: tid, isDelete: true}); - } - - if (threadState.pinned) { - ThreadTools.setPinnedState({tid: tid, isPinned: true}); - } - + ThreadTools.init = function(tid) { components.get('topic/delete').on('click', function() { - topicCommand(threadState.deleted ? 'restore' : 'delete', tid); + topicCommand('delete', tid); + return false; + }); + + components.get('topic/restore').on('click', function() { + topicCommand('restore', tid); return false; }); @@ -32,19 +23,29 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp }); components.get('topic/lock').on('click', function() { - socket.emit(threadState.locked ? 'topics.unlock' : 'topics.lock', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.lock', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + return false; + }); + + components.get('topic/unlock').on('click', function() { + socket.emit('topics.unlock', {tids: [tid], cid: ajaxify.variables.get('category_id')}); return false; }); components.get('topic/pin').on('click', function() { - socket.emit(threadState.pinned ? 'topics.unpin' : 'topics.pin', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + socket.emit('topics.pin', {tids: [tid], cid: ajaxify.variables.get('category_id')}); + return false; + }); + + components.get('topic/unpin').on('click', function() { + socket.emit('topics.unpin', {tids: [tid], cid: ajaxify.variables.get('category_id')}); return false; }); components.get('topic/mark-unread-for-all').on('click', function() { var btn = $(this); socket.emit('topics.markAsUnreadForAll', [tid], function(err) { - if(err) { + if (err) { return app.alertError(err.message); } app.alertSuccess('[[topic:markAsUnreadForAll.success]]'); @@ -60,12 +61,13 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp fork.init(); - components.get('topic').on('click', '[component="topic/follow"]', follow); + components.get('topic').on('click', '[component="topic/follow"], [component="topic/unfollow"]', follow); components.get('topic/follow').off('click').on('click', follow); + components.get('topic/unfollow').off('click').on('click', follow); function follow() { socket.emit('topics.toggleFollow', tid, function(err, state) { - if(err) { + if (err) { return app.alert({ type: 'danger', alert_id: 'topic_follow', @@ -101,21 +103,19 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp ThreadTools.setLockedState = function(data) { var threadEl = components.get('topic'); - if (parseInt(data.tid, 10) === parseInt(threadEl.attr('data-tid'), 10)) { - var isLocked = data.isLocked && !app.user.isAdmin; + if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) { + return; + } - components.get('topic/lock').translateHtml(' [[topic:thread_tools.' + (data.isLocked ? 'un': '') + 'lock]]'); + var isLocked = data.isLocked && !app.user.isAdmin; - translator.translate(isLocked ? '[[topic:locked]]' : '[[topic:reply]]', function(translated) { - var className = isLocked ? 'fa-lock' : 'fa-reply'; - threadEl.find('[component="post/reply"]').html(' ' + translated).attr('disabled', isLocked); - $('[component="topic/reply"]').attr('disabled', isLocked).html(isLocked ? ' ' + translated : translated); - }); + components.get('topic/lock').toggleClass('hidden', data.isLocked); + components.get('topic/unlock').toggleClass('hidden', !data.isLocked); + components.get('topic/reply').toggleClass('hidden', isLocked); + components.get('topic/reply/locked').toggleClass('hidden', !isLocked); - threadEl.find('[component="post/quote"], [component="post/edit"], [component="post/delete"]').toggleClass('hidden', isLocked); - $('[component="post/header"] i.fa-lock').toggleClass('hide', !data.isLocked); - ThreadTools.threadState.locked = data.isLocked; - } + threadEl.find('[component="post/reply"], [component="post/quote"], [component="post/edit"], [component="post/delete"]').toggleClass('hidden', isLocked); + $('[component="post/header"] i.fa-lock').toggleClass('hidden', !data.isLocked); }; ThreadTools.setDeleteState = function(data) { @@ -124,48 +124,28 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move', 'comp return; } - components.get('topic/delete').translateHtml(' [[topic:thread_tools.' + (data.isDelete ? 'restore' : 'delete') + ']]'); - - threadEl.toggleClass('deleted', data.isDelete); - ThreadTools.threadState.deleted = data.isDelete; - + components.get('topic/delete').toggleClass('hidden', data.isDelete); + components.get('topic/restore').toggleClass('hidden', !data.isDelete); components.get('topic/purge').toggleClass('hidden', !data.isDelete); + components.get('topic/deleted/message').toggleClass('hidden', !data.isDelete); - if (data.isDelete) { - translator.translate('[[topic:deleted_message]]', function(translated) { - $('
' + translated + '
').insertBefore(threadEl); - }); - } else { - $('#thread-deleted').remove(); - } + threadEl.toggleClass('deleted', data.isDelete); }; ThreadTools.setPinnedState = function(data) { var threadEl = components.get('topic'); - if (parseInt(data.tid, 10) === parseInt(threadEl.attr('data-tid'), 10)) { - translator.translate(' [[topic:thread_tools.' + (data.isPinned ? 'unpin' : 'pin') + ']]', function(translated) { - components.get('topic/pin').html(translated); - ThreadTools.threadState.pinned = data.isPinned; - }); - $('[component="post/header"] i.fa-thumb-tack').toggleClass('hide', !data.isPinned); + if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) { + return; } + + components.get('topic/pin').toggleClass('hidden', data.isPinned); + components.get('topic/unpin').toggleClass('hidden', !data.isPinned); + $('[component="post/header"] i.fa-thumb-tack').toggleClass('hidden', !data.isPinned); }; function setFollowState(state) { - var title = state ? '[[topic:unwatch.title]]' : '[[topic:watch.title]]'; - var iconClass = state ? 'fa fa-eye-slash' : 'fa fa-eye'; - var text = state ? '[[topic:unwatch]]' : '[[topic:watch]]'; - - var followEl = components.get('topic/follow'); - - translator.translate(title, function(titleTranslated) { - followEl.attr('title', titleTranslated).find('i').attr('class', iconClass); - followEl.find('span').text(text); - - translator.translate(followEl.html(), function(translated) { - followEl.html(translated); - }); - }); + components.get('topic/follow').toggleClass('hidden', state); + components.get('topic/unfollow').toggleClass('hidden', !state); } diff --git a/public/src/client/unread.js b/public/src/client/unread.js index fa97312c27..406116e9f1 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -124,7 +124,9 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' function createCategoryLinks(categories) { for (var i=0; i').text(title).html(), body: translated, modified: false, isMain: false, @@ -207,7 +230,7 @@ define('composer', [ pid: pid, uid: threadData.uid, handle: threadData.handle, - title: $('
').html(threadData.title).text(), + title: threadData.title, body: threadData.body, modified: false, isMain: threadData.isMain, @@ -276,10 +299,18 @@ define('composer', [ composer.bsEnvironment = utils.findBootstrapEnvironment(); + // see + // https://github.com/NodeBB/NodeBB/issues/2994 and + // https://github.com/NodeBB/NodeBB/issues/1951 + // remove when 1951 is resolved + + var title = postData.title.replace(/%/g, '%').replace(/,/g, ','); + var data = { + title: title, mobile: composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm', allowTopicsThumbnail: allowTopicsThumbnail, - showTags: isTopic || isMain, + isTopicOrMain: isTopic || isMain, minimumTagLength: config.minimumTagLength, maximumTagLength: config.maximumTagLength, isTopic: isTopic, @@ -305,11 +336,10 @@ define('composer', [ draft = drafts.getDraft(postData.save_id), submitBtn = postContainer.find('.composer-submit'); + preview.handleToggler(postContainer); tags.init(postContainer, composer.posts[post_uuid]); categoryList.init(postContainer, composer.posts[post_uuid]); - updateTitle(postData, postContainer); - activate(post_uuid); resize.reposition(postContainer); @@ -353,6 +383,7 @@ define('composer', [ postContainer.find('.composer-discard').on('click', function() { if (!composer.posts[post_uuid].modified) { + removeComposerHistory(); discard(post_uuid); return; } @@ -360,6 +391,7 @@ define('composer', [ translator.translate('[[modules:composer.discard]]', function(translated) { bootbox.confirm(translated, function(confirm) { if (confirm) { + removeComposerHistory(); discard(post_uuid); } btn.prop('disabled', false); @@ -382,15 +414,16 @@ define('composer', [ }); bodyEl.val(draft ? draft : postData.body); + preview.render(postContainer, function() { preview.matchScroll(postContainer); }); + drafts.init(postContainer, postData); resize.handleResize(postContainer); handleHelp(postContainer); - handleTogglePreview(postContainer); $(window).trigger('action:composer.loaded', { post_uuid: post_uuid @@ -419,48 +452,6 @@ define('composer', [ }); } - function handleTogglePreview(postContainer) { - var showBtn = postContainer.find('.write-container .toggle-preview'), - hideBtn = postContainer.find('.preview-container .toggle-preview'); - - hideBtn.on('click', function() { - $('.preview-container').addClass('hide'); - $('.write-container').addClass('maximized'); - showBtn.removeClass('hide'); - - $('.write').focus(); - }); - - showBtn.on('click', function() { - $('.preview-container').removeClass('hide'); - $('.write-container').removeClass('maximized'); - showBtn.addClass('hide'); - - $('.write').focus(); - }); - } - - function updateTitle(postData, postContainer) { - var titleEl = postContainer.find('.title'); - - if (parseInt(postData.tid, 10) > 0) { - titleEl.translateVal('[[topic:composer.replying_to, "' + postData.title + '"]]'); - titleEl.prop('disabled', true); - } else if (parseInt(postData.pid, 10) > 0) { - titleEl.val(postData.title); - titleEl.prop('disabled', true); - socket.emit('modules.composer.editCheck', postData.pid, function(err, editCheck) { - if (!err && editCheck.titleEditable) { - titleEl.prop('disabled', false); - } - }); - - } else { - titleEl.val(postData.title); - titleEl.prop('disabled', false); - } - } - function activate(post_uuid) { if(composer.active && composer.active !== post_uuid) { composer.minimize(composer.active); @@ -470,13 +461,11 @@ define('composer', [ } function focusElements(postContainer) { - var title = postContainer.find('.title'), - bodyEl = postContainer.find('textarea'); - - if (title.is(':disabled')) { - bodyEl.focus().putCursorAtEnd(); - } else { + var title = postContainer.find('input.title'); + if (title.length) { title.focus(); + } else { + postContainer.find('textarea').focus().putCursorAtEnd(); } } @@ -496,7 +485,7 @@ define('composer', [ thumbEl.val(thumbEl.val().trim()); } - var checkTitle = parseInt(postData.cid, 10) || parseInt(postData.pid, 10); + var checkTitle = (parseInt(postData.cid, 10) || parseInt(postData.pid, 10)) && postContainer.find('input.title').length; if (uploads.inProgress[post_uuid] && uploads.inProgress[post_uuid].length) { return composerAlert(post_uuid, '[[error:still-uploading]]'); @@ -578,6 +567,7 @@ define('composer', [ $('html').removeClass('composing mobile'); + } } @@ -589,6 +579,8 @@ define('composer', [ stopNotifyInterval(composer.posts[post_uuid]); stopNotifyTyping(composer.posts[post_uuid]); + + $('body').css({'margin-bottom': '0px'}); }; return composer; diff --git a/public/src/modules/composer/preview.js b/public/src/modules/composer/preview.js index 7efadc28c6..e6bab0cb4e 100644 --- a/public/src/modules/composer/preview.js +++ b/public/src/modules/composer/preview.js @@ -44,5 +44,40 @@ define('composer/preview', function() { preview.scrollTop(Math.max(preview[0].scrollHeight - preview.height(), 0) * scrollPercent); }; + preview.handleToggler = function(postContainer) { + function hidePreview() { + togglePreview(false); + localStorage.setItem('composer:previewToggled', true); + } + + function showPreview() { + togglePreview(true); + localStorage.removeItem('composer:previewToggled'); + } + + function togglePreview(show) { + previewContainer.toggleClass('hide', !show); + writeContainer.toggleClass('maximized', !show); + showBtn.toggleClass('hide', show); + + $('.write').focus(); + preview.matchScroll(postContainer); + } + + var showBtn = postContainer.find('.write-container .toggle-preview'), + hideBtn = postContainer.find('.preview-container .toggle-preview'), + previewContainer = $('.preview-container'), + writeContainer = $('.write-container'); + + hideBtn.on('click', hidePreview); + showBtn.on('click', showPreview); + + if (localStorage.getItem('composer:previewToggled')) { + hidePreview(); + } else { + showPreview(); + } + }; + return preview; }); \ No newline at end of file diff --git a/public/src/modules/composer/resize.js b/public/src/modules/composer/resize.js index 66f22462f3..23b5f67adb 100644 --- a/public/src/modules/composer/resize.js +++ b/public/src/modules/composer/resize.js @@ -37,7 +37,14 @@ define('composer/resize', ['autosize'], function(autosize) { } if (env === 'md' || env === 'lg') { - postContainer.css('transform', 'translate(0, ' + (Math.abs(1-percentage) * 100) + '%)'); + var transform = 'translate(0, ' + (Math.abs(1-percentage) * 100) + '%)'; + postContainer.css({ + '-webkit-transform': transform, + '-moz-transform': transform, + '-ms-transform': transform, + '-o-transform': transform, + 'transform': transform + }); } else { postContainer.removeAttr('style'); } diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 04937895c5..7d79265b84 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -14,17 +14,19 @@ properties = item.properties; if (properties) { - if (properties.loggedIn && !data.loggedIn || - properties.adminOnly && !data.isAdmin || - properties.installed && properties.installed.search && !data.searchEnabled) { + if ((properties.loggedIn && !data.loggedIn) || + (properties.adminOnly && !data.isAdmin) || + (properties.installed && properties.installed.search && !data.searchEnabled)) { return false; } } - if (item.route.match('/users')) { - if (data.privateUserInfo && !data.isAdmin) { - return false; - } + if (item.route.match('/users') && data.privateUserInfo && !data.loggedIn) { + return false; + } + + if (item.route.match('/tags') && data.privateTagListing && !data.loggedIn) { + return false; } return true; @@ -44,31 +46,41 @@ }; helpers.escape = function(str) { - var utils = utils || require('../utils'); - return utils.escapeHTML(str); + if (typeof utils !== 'undefined') { + return utils.escapeHTML(str); + } else { + return require('../utils').escapeHTML(str); + } }; helpers.stripTags = function(str) { - var S = S || require('string'); - return S(str).stripTags().s; + if (typeof S !== 'undefined') { + return S(str).stripTags().s; + } else { + var S = require('string'); + return S(str).stripTags().s; + } }; helpers.generateCategoryBackground = function(category) { var style = []; - if (category.backgroundImage) { - style.push('background-image: url(' + category.backgroundImage + ')'); - } - if (category.bgColor) { - style.push('background-color: ' + category.bgColor + ';'); + style.push('background-color: ' + category.bgColor); } if (category.color) { - style.push('color: ' + category.color + ';'); + style.push('color: ' + category.color); } - return style.join(' '); + if (category.backgroundImage) { + style.push('background-image: url(' + category.backgroundImage + ')'); + if (category.imageClass) { + style.push('background-size: ' + category.imageClass); + } + } + + return style.join('; ') + ';'; }; helpers.generateTopicClass = function(topic) { @@ -93,6 +105,10 @@ return style.join(' '); }; + helpers.getBookmarkFromIndex = function(topic) { + return (topic.index || 0) + 1; + }; + // Groups helpers helpers.membershipBtn = function(groupObj) { if (groupObj.isMember) { diff --git a/public/src/modules/iconSelect.js b/public/src/modules/iconSelect.js index 5dea30c189..c8cd3ac529 100644 --- a/public/src/modules/iconSelect.js +++ b/public/src/modules/iconSelect.js @@ -25,7 +25,7 @@ define('iconSelect', function() { title: 'Select an Icon', buttons: { success: { - label: 'Save', + label: 'Select', callback: function(confirm) { var iconClass = $('.bootbox .selected').attr('class'); var categoryIconClass = $('
').addClass(iconClass).removeClass('fa').removeClass('selected').attr('class'); diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 9404c885d9..443f404cea 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -84,7 +84,7 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com return; } - $('.pagination-block').toggleClass('hidden', !flag); + $('.pagination-block').toggleClass('invisible', !flag); } navigator.update = function() { @@ -106,6 +106,10 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com } }); + if (topIndex && !bottomIndex) { + bottomIndex = topIndex; + } + if (typeof navigator.callback === 'function' && topIndex && bottomIndex) { index = navigator.callback(topIndex, bottomIndex, count); navigator.updateTextAndProgressBar(); @@ -157,7 +161,8 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com var elTop = el.offset().top; var elBottom = elTop + Math.floor(el.height()); - return (elTop >= scrollTop && elBottom <= scrollBottom) || (elTop <= scrollTop && elBottom >= scrollTop); + + return (elTop >= scrollTop && elBottom < scrollBottom) || (elTop < scrollTop && elBottom > scrollTop); } navigator.scrollToPost = function(postIndex, highlight, duration, offset) { @@ -169,17 +174,13 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com duration = duration !== undefined ? duration : 400; navigator.scrollActive = true; - if(components.get('post/anchor', postIndex).length) { + if (components.get('post/anchor', postIndex).length) { return scrollToPid(postIndex, highlight, duration, offset); } if (config.usePagination) { - if (window.location.search.indexOf('page') !== -1) { - navigator.update(); - return; - } - var page = Math.ceil((postIndex + 1) / config.postsPerPage); + var page = Math.max(1, Math.ceil(postIndex / config.postsPerPage)); if(parseInt(page, 10) !== pagination.currentPage) { pagination.loadPage(page, function() { @@ -198,7 +199,7 @@ define('navigator', ['forum/pagination', 'components'], function(pagination, com function scrollToPid(postIndex, highlight, duration, offset) { var scrollTo = components.get('post/anchor', postIndex); - if (!scrollTo) { + if (!scrollTo.length) { navigator.scrollActive = false; return; } diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 3f342a1207..c3a9b984de 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -39,12 +39,15 @@ define('notifications', ['sounds', 'translator'], function(sound, translator) { }); notifList.on('click', '[data-nid]', function() { - var nid = this.getAttribute('data-nid'); - - socket.emit('notifications.markRead', nid, function(err) { + var unread = $(this).hasClass('unread'); + if (!unread) { + return; + } + socket.emit('notifications.markRead', $(this).attr('data-nid'), function(err) { if (err) { - app.alertError(err.message); + return app.alertError(err.message); } + increaseNotifCount(-1); }); }); @@ -58,14 +61,13 @@ define('notifications', ['sounds', 'translator'], function(sound, translator) { }); notifList.on('click', '.mark-read', function(e) { - var liEl = $(this.parentNode), - nid = liEl.attr('data-nid'), + var liEl = $(this).parent(), unread = liEl.hasClass('unread'); e.preventDefault(); e.stopPropagation(); - socket.emit('notifications.mark' + (unread ? 'Read' : 'Unread'), nid, function(err) { + socket.emit('notifications.mark' + (unread ? 'Read' : 'Unread'), liEl.attr('data-nid'), function(err) { if (err) { app.alertError(err.message); } diff --git a/public/src/modules/search.js b/public/src/modules/search.js index e17b234656..f7ae2243f4 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -88,13 +88,15 @@ define('search', ['navigator', 'translator'], function(nav, translator) { term: term }; - Search.topicDOM.update(0); + Search.checkPagePresence(tid, function() { + Search.topicDOM.update(0); + }); } }); }; Search.checkPagePresence = function(tid, callback) { - if (!ajaxify.currentPage.match(new RegExp('^topic/' + tid))) { + if (parseInt(ajaxify.variables.get('topic_id'), 10) !== parseInt(tid, 10)) { ajaxify.go('topic/' + tid, callback); } else { callback(); @@ -119,20 +121,18 @@ define('search', ['navigator', 'translator'], function(nav, translator) { Search.topicDOM.start(); - Search.checkPagePresence(Search.current.tid, function() { - if (Search.current.results.length > 0) { - topicSearchEl.find('.count').html((index+1) + ' / ' + Search.current.results.length); - topicSearchEl.find('.prev, .next').removeAttr('disabled'); - socket.emit('posts.getPidIndex', Search.current.results[index], function(err, postIndex) { - nav.scrollToPost(postIndex-1, true); // why -1? Ask @barisusakli - }); - } else { - translator.translate('[[search:no-matches]]', function(text) { - topicSearchEl.find('.count').html(text); - }); - topicSearchEl.removeClass('hidden').find('.prev, .next').attr('disabled', 'disabled'); - } - }); + if (Search.current.results.length > 0) { + topicSearchEl.find('.count').html((index+1) + ' / ' + Search.current.results.length); + topicSearchEl.find('.prev, .next').removeAttr('disabled'); + socket.emit('posts.getPidIndex', Search.current.results[index], function(err, postIndex) { + nav.scrollToPost(postIndex-1, true); // why -1? Ask @barisusakli + }); + } else { + translator.translate('[[search:no-matches]]', function(text) { + topicSearchEl.find('.count').html(text); + }); + topicSearchEl.removeClass('hidden').find('.prev, .next').attr('disabled', 'disabled'); + } }; Search.topicDOM.start = function() { diff --git a/public/src/modules/settings.js b/public/src/modules/settings.js index 8c7d74a448..17302ea1d1 100644 --- a/public/src/modules/settings.js +++ b/public/src/modules/settings.js @@ -9,7 +9,8 @@ define('settings', function () { 'settings/textarea', 'settings/select', 'settings/array', - 'settings/key' + 'settings/key', + 'settings/object' ]; var Settings, diff --git a/public/src/modules/settings/object.js b/public/src/modules/settings/object.js new file mode 100644 index 0000000000..8c7a53f99a --- /dev/null +++ b/public/src/modules/settings/object.js @@ -0,0 +1,116 @@ +define('settings/object', function () { + + var Settings = null, + SettingsObject, + helper = null; + + /** + Creates a new child-element of given property with given data and calls given callback with elements to add. + @param field Any wrapper that contains all properties of the object. + @param key The key of the object. + @param attributes The attributes to call {@link Settings.helper.createElementOfType} with or to add as + element-attributes. + @param value The value to call {@link Settings.helper.fillField} with. + @param separator The separator to use. + @param insertCb The callback to insert the elements. + */ + function addObjectPropertyElement(field, key, attributes, prop, value, separator, insertCb) { + var prepend = attributes['data-prepend'], + append = attributes['data-append'], + type, element; + delete attributes['data-prepend']; + delete attributes['data-append']; + attributes = helper.deepClone(attributes); + type = attributes['data-type'] || attributes.type || 'text', + element = $(helper.createElementOfType(type, attributes.tagName, attributes)); + element.attr('data-parent', '_' + key); + element.attr('data-prop', prop); + delete attributes['data-type']; + delete attributes['tagName']; + for (var name in attributes) { + var val = attributes[name]; + if (name.search('data-') === 0) { + element.data(name.substring(5), val); + } else if (name.search('prop-') === 0) { + element.prop(name.substring(5), val); + } else { + element.attr(name, val); + } + } + helper.fillField(element, value); + if ($('[data-parent="_' + key + '"]', field).length) { + insertCb(separator); + } + if (prepend) { + insertCb(prepend); + } + insertCb(element); + if (append) { + insertCb(append); + } + } + + SettingsObject = { + types: ['object'], + use: function () { + helper = (Settings = this).helper; + }, + create: function (ignored, tagName) { + return helper.createElement(tagName || 'div'); + }, + set: function (element, value) { + var properties = element.data('attributes') || element.data('properties'), + key = element.data('key') || element.data('parent'), + separator = element.data('split') || ', ', + propertyIndex, propertyName, attributes; + separator = (function () { + try { + return $(separator); + } catch (_error) { + return $(document.createTextNode(separator)); + } + })(); + element.empty(); + if (typeof value !== 'object') { + value = {}; + } + if (Array.isArray(properties)) { + for (propertyIndex in properties) { + attributes = properties[propertyIndex]; + if (typeof attributes !== 'object') { + attributes = {}; + } + propertyName = attributes['data-prop'] || attributes['data-property'] || propertyIndex; + if (value[propertyName] === void 0 && attributes['data-new'] !== void 0) { + value[propertyName] = attributes['data-new']; + } + addObjectPropertyElement(element, key, attributes, propertyName, value[propertyName], separator.clone(), function (el) { + element.append(el); + }); + } + } + }, + get: function (element, trim, empty) { + var key = element.data('key') || element.data('parent'), + properties = $('[data-parent="_' + key + '"]', element), + value = {}; + properties.each(function (i, property) { + property = $(property); + var val = helper.readValue(property), + prop = property.data('prop'), + empty = helper.isTrue(property.data('empty')); + if (empty || val !== void 0 && (val == null || val.length !== 0)) { + return value[prop] = val; + } + }); + if (empty || Object.keys(value).length) { + return value; + } else { + return void 0; + } + } + }; + + return SettingsObject; + +}); diff --git a/public/src/modules/sort.js b/public/src/modules/sort.js index e063fceda3..6033b9680a 100644 --- a/public/src/modules/sort.js +++ b/public/src/modules/sort.js @@ -1,16 +1,16 @@ 'use strict'; /* globals define, config, socket, app, ajaxify, templates */ -define('sort', function() { +define('sort', ['components'], function(components) { var module = {}; module.handleSort = function (field, method, gotoOnSave) { - var threadSort = $('.thread-sort'); + var threadSort = components.get('thread/sort'); threadSort.find('i').removeClass('fa-check'); var currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]'); currentSetting.find('i').addClass('fa-check'); - $('.thread-sort').on('click', 'a', function() { + threadSort.on('click', 'a', function() { var newSetting = $(this).attr('data-sort'); socket.emit(method, newSetting, function(err) { if (err) { diff --git a/public/src/modules/taskbar.js b/public/src/modules/taskbar.js index 470b5144b6..f1757fe553 100644 --- a/public/src/modules/taskbar.js +++ b/public/src/modules/taskbar.js @@ -46,30 +46,18 @@ define('taskbar', function() { taskbar.push = function(module, uuid, options) { var element = taskbar.tasklist.find('li[data-uuid="' + uuid + '"]'); - if (element.length) { - return; - } - - var title = $('
').text(options.title || 'NodeBB Task').html(); + var data = { + module: module, + uuid: uuid, + options: options, + element: element + }; - var btnEl = $('
  • ') - .html('' + - (options.icon ? ' ' : '') + - (options.image ? ' ': '') + - '' + title + '' + - '') - .attr({ - 'data-module': module, - 'data-uuid': uuid - }) - .addClass(options.state !== undefined ? options.state : 'active'); + $(window).trigger('filter:taskbar.push', data); - if (!options.state || options.state === 'active') { - minimizeAll(); + if (!element.length) { + createTaskbar(data); } - - taskbar.tasklist.append(btnEl); - update(); }; taskbar.minimize = function(module, uuid) { @@ -107,5 +95,32 @@ define('taskbar', function() { taskbar.tasklist.find('.active').removeClass('active'); } + function createTaskbar(data) { + var title = $('
    ').text(data.options.title || 'NodeBB Task').html(); + + var taskbarEl = $('
  • ') + .addClass(data.options.className) + .html('' + + (data.options.icon ? ' ' : '') + + (data.options.image ? ' ': '') + + '' + title + '' + + '') + .attr({ + 'data-module': data.module, + 'data-uuid': data.uuid + }) + .addClass(data.options.state !== undefined ? data.options.state : 'active'); + + if (!data.options.state || data.options.state === 'active') { + minimizeAll(); + } + + taskbar.tasklist.append(taskbarEl); + update(); + + data.element = taskbarEl; + $(window).trigger('action:taskbar.pushed', data); + } + return taskbar; }); diff --git a/public/src/modules/uploader.js b/public/src/modules/uploader.js index 6efdbd988c..63fa21cbce 100644 --- a/public/src/modules/uploader.js +++ b/public/src/modules/uploader.js @@ -16,8 +16,9 @@ define('uploader', ['csrf'], function(csrf) { uploadForm.find('#params').val(JSON.stringify(params)); if (fileSize) { - uploadForm.find('#upload-file-size').html(fileSize); - uploadForm.find('#file-size-block').removeClass('hide'); + uploadForm.find('#file-size-block') + .translateText('[[uploads:maximum-file-size, ' + fileSize + ']]') + .removeClass('hide'); } else { uploadForm.find('#file-size-block').addClass('hide'); } diff --git a/public/vendor/bootstrap/js/bootstrap.min.js b/public/vendor/bootstrap/js/bootstrap.min.js index d839865900..c8f82e592a 100644 --- a/public/vendor/bootstrap/js/bootstrap.min.js +++ b/public/vendor/bootstrap/js/bootstrap.min.js @@ -1,7 +1,7 @@ /*! - * Bootstrap v3.3.1 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.1",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.1",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.1",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c="prev"==a?-1:1,d=this.getItemIndex(b),e=(d+c)%this.$items.length;return this.$items.eq(e)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i="next"==b?"first":"last",j=this;if(!f.length){if(!this.options.wrap)return;f=this.$element.find(".item")[i]()}if(f.hasClass("active"))return this.sliding=!1;var k=f[0],l=a.Event("slide.bs.carousel",{relatedTarget:k,direction:h});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var m=a(this.$indicators.children()[this.getItemIndex(f)]);m&&m.addClass("active")}var n=a.Event("slid.bs.carousel",{relatedTarget:k,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),j.sliding=!1,setTimeout(function(){j.$element.trigger(n)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(n)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.1",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.find("> .panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.1",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('
  • ");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('
  • ').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b}(jQuery),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c){return a.map(c,function(a){return new b(a)})},a.extend(b.prototype,{match:null,replace:null,search:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(jQuery),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var a=this._getCaretRelativePosition(),b=this.$el.offset();return a.top+=b.top,a.left+=b.left,a},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(jQuery),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}b.DIV_PROPERTIES={left:-9999,position:"absolute",top:0,whiteSpace:"pre-wrap"},b.COPY_PROPERTIES=["border-width","font-family","font-size","font-style","font-variant","font-weight","height","letter-spacing","word-spacing","line-height","text-decoration","text-align","width","padding-top","padding-right","padding-bottom","padding-left","margin-top","margin-right","margin-bottom","margin-left","border-style","box-sizing","tab-size"],a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=this.el.value.substring(this.el.selectionEnd),f=c.replace(b);a.isArray(f)&&(e=f[1]+e,f=f[0]),d=d.replace(c.match,f),this.$el.val(d+e),this.el.selectionStart=this.el.selectionEnd=d.length},_getCaretRelativePosition:function(){var b=a("
    ").css(this._copyCss()).text(this.getTextFromHeadToCaret()),c=a("").text(".").appendTo(b);this.$el.before(b);var d=c.position();return d.top+=c.height()-this.$el.scrollTop(),d.lineHeight=c.height(),b.remove(),d},_copyCss:function(){return a.extend({overflow:this.el.scrollHeight>this.el.offsetHeight?"scroll":"auto"},b.DIV_PROPERTIES,this._getStyles())},_getStyles:function(a){var c=a("
    ").css(["color"]).color;return"undefined"!=typeof c?function(){return this.$el.css(b.COPY_PROPERTIES)}:function(){var c=this.$el,d={};return a.each(b.COPY_PROPERTIES,function(a,b){d[b]=c.css(b)}),d}}(a),getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)}}),a.fn.textcomplete.Textarea=b}(jQuery),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a(""+c+"").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=this.el.value.substring(d.length),f=c.replace(b);a.isArray(f)&&(e=f[1]+e,f=f[0]),d=d.replace(c.match,f),this.$el.val(d+e),this.el.focus();var g=this.el.createTextRange();g.collapse(!0),g.moveEnd("character",d.length),g.moveStart("character",d.length),g.select()},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(jQuery),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=window.getSelection(),f=e.getRangeAt(0),g=f.cloneRange();g.selectNodeContents(f.startContainer);var h=g.toString(),i=h.substring(f.startOffset),j=c.replace(b);a.isArray(j)&&(i=j[1]+i,j=j[0]),d=d.replace(c.match,j),f.selectNodeContents(f.startContainer),f.deleteContents();var k=document.createTextNode(d+i);f.insertNode(k),f.setStart(k,d.length),f.collapse(!0),e.removeAllRanges(),e.addRange(f)},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),d.remove();var f=this.$el.attr("dir")||this.$el.css("direction");return"rtl"===f&&(e.left-=this.listView.$el.width()),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(jQuery); +/*! jquery-textcomplete - v0.1.2 - 2014-02-08 */!function(a){"use strict";var b=function(a){var b,d;return b=function(){d=!1},function(){var e;d||(d=!0,e=c(arguments),e.unshift(b),a.apply(this,e))}},c=function(a){var b;return b=Array.prototype.slice.call(a)},d=function(){var b;return b=a("
    ").css(["color"]).color,"undefined"!=typeof b?function(a,b){return a.css(b)}:function(b,c){var d;return d={},a.each(c,function(a,c){d[c]=b.css(c)}),d}}(),e=function(a){return a},f=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}},g=function(a,b){var c,d;if(a.indexOf)return-1!=a.indexOf(b);for(c=0,d=a.length;d>c;c++)if(a[c]===b)return!0;return!1},h=function(){function c(b){var c,d,e;d=h.clone(),this.el=b.get(0),this.$el=b,this.id="textComplete"+j++,c=k(this.$el),e=this.el===document.activeElement,this.$el.wrap(c).before(d),e&&this.el.focus(),this.listView=new i(d,this),this.strategies=[],this.$el.on({"keyup.textComplete":a.proxy(this.onKeyup,this),"keydown.textComplete":a.proxy(this.listView.onKeydown,this.listView)}),a(document).on("click."+this.id,a.proxy(this.onClickDocument,this)).on("keyup."+this.id,a.proxy(this.onKeyupDocument,this))}var e,f,g,h,j;e={wrapper:'
    ',list:''},f={wrapper:{position:"relative"},list:{position:"absolute",top:0,left:0,zIndex:"100",display:"none"}},g=a(e.wrapper).css(f.wrapper),h=a(e.list).css(f.list),j=0,a.extend(c.prototype,{register:function(a){this.strategies=this.strategies.concat(a)},renderList:function(a){this.clearAtNext&&(this.listView.clear(),this.clearAtNext=!1),a.length&&(this.listView.shown||(this.listView.setPosition(this.getCaretPosition()).clear().activate(),this.listView.strategy=this.strategy),a=a.slice(0,this.strategy.maxCount),this.listView.render(a)),!this.listView.data.length&&this.listView.shown&&this.listView.deactivate()},searchCallbackFactory:function(a){var b=this;return function(c,d){b.renderList(c),d||(a(),b.clearAtNext=!0)}},onKeyup:function(a){var b,c;if(!this.skipSearch(a))if(b=this.extractSearchQuery(this.getTextFromHeadToCaret()),b.length){if(c=b[1],this.term===c)return;this.term=c,this.search(b)}else this.term=null,this.listView.deactivate()},skipSearch:function(a){if(this.skipNextKeyup)return this.skipNextKeyup=!1,!0;switch(a.keyCode){case 40:case 38:return!0}},onSelect:function(b){var c,d,e;c=this.getTextFromHeadToCaret(),d=this.el.value.substring(this.el.selectionEnd),e=this.strategy.replace(b),a.isArray(e)&&(d=e[1]+d,e=e[0]),c=c.replace(this.strategy.match,e),this.$el.val(c+d).trigger("textComplete:select",b),this.el.focus(),this.el.selectionStart=this.el.selectionEnd=c.length,this.skipNextKeyup=!0},onClickDocument:function(a){a.originalEvent&&!a.originalEvent.keepTextCompleteDropdown&&this.listView.deactivate()},onKeyupDocument:function(a){this.listView.shown&&27===a.keyCode&&(this.listView.deactivate(),this.$el.focus())},destroy:function(){var b;this.$el.off("keyup.textComplete").off("keydown.textComplete"),a(document).off("click."+this.id).off("keyup."+this.id),this.listView.destroy(),b=this.$el.parent(),b.after(this.$el).remove(),this.$el.data("textComplete",void 0),this.$el=null},getCaretPosition:function(){if(0!==this.el.selectionEnd){var b,c,e,f,g;return b=["border-width","font-family","font-size","font-style","font-variant","font-weight","height","letter-spacing","word-spacing","line-height","text-decoration","width","padding-top","padding-right","padding-bottom","padding-left","margin-top","margin-right","margin-bottom","margin-left"],c=a.extend({position:"absolute",overflow:"auto","white-space":"pre-wrap",top:0,left:-9999},d(this.$el,b)),e=a("
    ").css(c).text(this.getTextFromHeadToCaret()),f=a("").text(" ").appendTo(e),this.$el.before(e),g=f.position(),g.top+=f.height()-this.$el.scrollTop(),e.remove(),g}},getTextFromHeadToCaret:function(){var a,b,c;return b=this.el.selectionEnd,"number"==typeof b?a=this.el.value.substring(0,b):document.selection&&(c=this.el.createTextRange(),c.moveStart("character",0),c.moveEnd("textedit"),a=c.text),a},extractSearchQuery:function(a){var b,c,d,e;for(b=0,c=this.strategies.length;c>b;b++)if(d=this.strategies[b],e=a.match(d.match))return[d,e[d.index]];return[]},search:b(function(a,b){var c;this.strategy=b[0],c=b[1],this.strategy.search(c,this.searchCallbackFactory(a))})});var k=function(a){return g.clone().css("display",a.css("display"))};return c}(),i=function(){function b(b,c){this.$el=b,this.index=0,this.completer=c,this.$el.on("click.textComplete","li.textcomplete-item",a.proxy(this.onClick,this))}return a.extend(b.prototype,{shown:!1,render:function(a){var b,c,d,e,f;for(b="",c=0,d=a.length;d>c&&(f=a[c],g(this.data,f)||(e=this.data.length,this.data.push(f),b+='
  • ',b+=this.strategy.template(f),b+="
  • ",this.data.length!==this.strategy.maxCount));c++);this.$el.append(b),this.data.length?this.activateIndexedItem():this.deactivate()},clear:function(){return this.data=[],this.$el.html(""),this.index=0,this},activateIndexedItem:function(){this.$el.find(".active").removeClass("active"),this.getActiveItem().addClass("active")},getActiveItem:function(){return a(this.$el.children().get(this.index))},activate:function(){return this.shown||(this.$el.show(),this.completer.$el.trigger("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.completer.$el.trigger("textComplete:hide"),this.shown=!1,this.data=this.index=null),this},setPosition:function(a){return this.$el.css(a),this},select:function(a){this.completer.onSelect(this.data[a]),this.deactivate()},onKeydown:function(a){this.shown&&(38===a.keyCode?(a.preventDefault(),0===this.index?this.index=this.data.length-1:this.index-=1,this.activateIndexedItem()):40===a.keyCode?(a.preventDefault(),this.index===this.data.length-1?this.index=0:this.index+=1,this.activateIndexedItem()):(13===a.keyCode||9===a.keyCode)&&(a.preventDefault(),this.select(parseInt(this.getActiveItem().data("index"),10))))},onClick:function(b){var c=a(b.target);b.originalEvent.keepTextCompleteDropdown=!0,c.hasClass("textcomplete-item")||(c=c.parents("li.textcomplete-item")),this.select(parseInt(c.data("index"),10))},destroy:function(){this.deactivate(),this.$el.off("click.textComplete").remove(),this.$el=null}}),b}();a.fn.textcomplete=function(b){var c,d,g,i;if(i="textComplete","destroy"===b)return this.each(function(){var b=a(this).data(i);b&&b.destroy()});for(c=0,d=b.length;d>c;c++)g=b[c],g.template||(g.template=e),null==g.index&&(g.index=2),g.cache&&(g.search=f(g.search)),g.maxCount||(g.maxCount=10);return this.each(function(){var c,d;c=a(this),d=c.data(i),d||(d=new h(c),c.data(i,d)),d.register(b)})}}(window.jQuery||window.Zepto); diff --git a/src/batch.js b/src/batch.js index 096c1d7147..70ccd8df01 100644 --- a/src/batch.js +++ b/src/batch.js @@ -28,7 +28,7 @@ var async = require('async'), var batch = options.batch || DEFAULT_BATCH_SIZE; var start = 0; - var end = batch; + var stop = batch; var done = false; async.whilst( @@ -36,11 +36,11 @@ var async = require('async'), return !done; }, function(next) { - db.getSortedSetRange(setKey, start, end, function(err, ids) { + db.getSortedSetRange(setKey, start, stop, function(err, ids) { if (err) { return next(err); } - if (!ids.length || options.doneIf(start, end, ids)) { + if (!ids.length || options.doneIf(start, stop, ids)) { done = true; return next(); } @@ -49,7 +49,7 @@ var async = require('async'), return next(err); } start += utils.isNumber(options.alwaysStartAt) ? options.alwaysStartAt : batch + 1; - end = start + batch; + stop = start + batch; next(); }); }); diff --git a/src/categories.js b/src/categories.js index 3061ef8425..6112c5dc58 100644 --- a/src/categories.js +++ b/src/categories.js @@ -38,14 +38,7 @@ var async = require('async'), async.parallel({ topics: function(next) { - Categories.getCategoryTopics({ - cid: data.cid, - set: data.set, - reverse: data.reverse, - start: data.start, - stop: data.end, - uid: data.uid - }, next); + Categories.getCategoryTopics(data, next); }, pageCount: function(next) { Categories.getPageCount(data.cid, data.uid, next); @@ -65,7 +58,7 @@ var async = require('async'), plugins.fireHook('filter:category.get', {category: category, uid: data.uid}, function(err, data) { callback(err, data ? data.category : null); - }); + }); }); }); }; @@ -120,7 +113,7 @@ var async = require('async'), }, function(cids, next) { Categories.getCategories(cids, uid, next); - } + } ], callback); }; @@ -248,6 +241,7 @@ var async = require('async'), categories[i]['unread-class'] = (parseInt(categories[i].topic_count, 10) === 0 || (hasRead[i] && uid !== 0)) ? '' : 'unread'; categories[i].children = results.children[i]; categories[i].parent = results.parents[i] && !results.parents[i].disabled ? results.parents[i] : null; + calculateTopicPostCount(categories[i]); } } @@ -255,6 +249,21 @@ var async = require('async'), }); }; + function calculateTopicPostCount(category) { + if (!Array.isArray(category.children) || !category.children.length) { + return; + } + var postCount = parseInt(category.post_count, 10) || 0; + var topicCount = parseInt(category.topic_count, 10) || 0; + + category.children.forEach(function(child) { + postCount += parseInt(child.post_count, 10) || 0; + topicCount += parseInt(child.topic_count, 10) || 0; + }); + category.post_count = postCount; + category.topic_count = topicCount; + } + Categories.getParents = function(cids, callback) { Categories.getMultipleCategoryFields(cids, ['parentCid'], function(err, data) { if (err) { @@ -262,7 +271,11 @@ var async = require('async'), } var parentCids = data.map(function(category) { - return category && category.parentCid; + if (category && category.hasOwnProperty('parentCid') && category.parentCid) { + return category.parentCid; + } else { + return 0; + } }); Categories.getCategoriesData(parentCids, callback); diff --git a/src/categories/create.js b/src/categories/create.js index 0db3cad0c3..ee752a4802 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -20,19 +20,19 @@ module.exports = function(Categories) { var category = { cid: cid, name: data.name, - description: data.description, - icon: data.icon, + description: ( data.description ? data.description : '' ), + icon: ( data.icon ? data.icon : '' ), bgColor: data.bgColor || colours[0], color: data.color || colours[1], slug: slug, - parentCid: 0, + parentCid: ( data.parentCid ? data.parentCid : 0 ), topic_count: 0, post_count: 0, disabled: 0, order: order, link: '', numRecentReplies: 1, - class: 'col-md-3 col-xs-12', + class: ( data.class ? data.class : 'col-md-3 col-xs-6' ), imageClass: 'auto' }; diff --git a/src/categories/update.js b/src/categories/update.js index f68a7aed20..6fcf7d4b57 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -3,8 +3,8 @@ var async = require('async'), db = require('../database'), - utils = require('../../public/src/utils'); - + utils = require('../../public/src/utils'), + plugins = require('../plugins'); module.exports = function(Categories) { @@ -16,12 +16,12 @@ module.exports = function(Categories) { return next(err); } - var category = modified[cid]; - var fields = Object.keys(category); - - async.each(fields, function(key, next) { - updateCategoryField(cid, key, category[key], next); - }, next); + plugins.fireHook('filter:category.update', modified[cid], function(err, category) { + var fields = Object.keys(category); + async.each(fields, function(key, next) { + updateCategoryField(cid, key, category[key], next); + }, next); + }); }); } diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 51cc3daf82..dcff0f5ad2 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -3,8 +3,6 @@ var accountsController = {}; var fs = require('fs'), - path = require('path'), - winston = require('winston'), nconf = require('nconf'), async = require('async'), validator = require('validator'), @@ -15,13 +13,11 @@ var fs = require('fs'), topics = require('../topics'), groups = require('../groups'), messaging = require('../messaging'), - postTools = require('../postTools'), utils = require('../../public/src/utils'), meta = require('../meta'), plugins = require('../plugins'), languages = require('../languages'), - image = require('../image'), - file = require('../file'), + helpers = require('./helpers'); function getUserDataByUserSlug(userslug, callerUID, callback) { @@ -92,10 +88,17 @@ function getUserDataByUserSlug(userslug, callerUID, callback) { userData.profile_links = results.profile_links; userData.status = require('../socket.io').isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline'; userData.banned = parseInt(userData.banned, 10) === 1; - userData.websiteName = userData.website.replace(validator.escape('http://'), '').replace(validator.escape('https://'), ''); + userData.websiteName = userData.website.replace('http://', '').replace('https://', ''); userData.followingCount = parseInt(userData.followingCount, 10) || 0; userData.followerCount = parseInt(userData.followerCount, 10) || 0; + userData.username = validator.escape(userData.username); + userData.email = validator.escape(userData.email); + userData.fullname = validator.escape(userData.fullname); + userData.location = validator.escape(userData.location); + userData.signature = validator.escape(userData.signature); + userData.aboutme = validator.escape(userData.aboutme || ''); + callback(null, userData); }); }); @@ -120,8 +123,7 @@ accountsController.getUserByUID = function(req, res, next) { }; accountsController.getAccount = function(req, res, next) { - var lowercaseSlug = req.params.userslug.toLowerCase(), - callerUID = req.user ? parseInt(req.user.uid, 10) : 0; + var lowercaseSlug = req.params.userslug.toLowerCase(); if (req.params.userslug !== lowercaseSlug) { if (res.locals.isAPI) { @@ -131,7 +133,7 @@ accountsController.getAccount = function(req, res, next) { } } - getUserDataByUserSlug(req.params.userslug, callerUID, function (err, userData) { + getUserDataByUserSlug(req.params.userslug, req.uid, function (err, userData) { if (err) { return next(err); } @@ -140,19 +142,26 @@ accountsController.getAccount = function(req, res, next) { return helpers.notFound(req, res); } - if (callerUID !== parseInt(userData.uid, 10)) { + if (req.uid !== parseInt(userData.uid, 10)) { user.incrementUserFieldBy(userData.uid, 'profileviews', 1); } async.parallel({ isFollowing: function(next) { - user.isFollowing(callerUID, userData.theirid, next); + user.isFollowing(req.uid, userData.theirid, next); }, posts: function(next) { - posts.getPostsFromSet('uid:' + userData.theirid + ':posts', callerUID, 0, 9, next); + posts.getPostsFromSet('uid:' + userData.theirid + ':posts', req.uid, 0, 9, next); }, signature: function(next) { - postTools.parseSignature(userData, callerUID, next); + posts.parseSignature(userData, req.uid, next); + }, + aboutme: function(next) { + if (userData.aboutme) { + plugins.fireHook('filter:parse.raw', userData.aboutme, next); + } else { + next(); + } } }, function(err, results) { if(err) { @@ -162,7 +171,7 @@ accountsController.getAccount = function(req, res, next) { userData.posts = results.posts.posts.filter(function (p) { return p && parseInt(p.deleted, 10) !== 1; }); - + userData.aboutme = results.aboutme; userData.nextStart = results.posts.nextStart; userData.isFollowing = results.isFollowing; @@ -170,7 +179,7 @@ accountsController.getAccount = function(req, res, next) { userData.profileviews = 1; } - plugins.fireHook('filter:user.account', {userData: userData, uid: callerUID}, function(err, data) { + plugins.fireHook('filter:user.account', {userData: userData, uid: req.uid}, function(err, data) { if (err) { return next(err); } @@ -189,12 +198,11 @@ accountsController.getFollowers = function(req, res, next) { }; function getFollow(tpl, name, req, res, next) { - var callerUID = req.user ? parseInt(req.user.uid, 10) : 0; var userData; async.waterfall([ function(next) { - getUserDataByUserSlug(req.params.userslug, callerUID, next); + getUserDataByUserSlug(req.params.userslug, req.uid, next); }, function(data, next) { userData = data; @@ -205,7 +213,7 @@ function getFollow(tpl, name, req, res, next) { user[method](userData.uid, 0, 49, next); } ], function(err, users) { - if(err) { + if (err) { return next(err); } @@ -233,9 +241,7 @@ accountsController.getTopics = function(req, res, next) { }; accountsController.getGroups = function(req, res, next) { - var callerUID = req.user ? parseInt(req.user.uid, 10) : 0; - - accountsController.getBaseUser(req.params.userslug, callerUID, function(err, userData) { + accountsController.getBaseUser(req.params.userslug, req.uid, function(err, userData) { if (err) { return next(err); } @@ -257,24 +263,51 @@ accountsController.getGroups = function(req, res, next) { }; function getFromUserSet(tpl, set, method, type, req, res, next) { - var callerUID = req.user ? parseInt(req.user.uid, 10) : 0; - - accountsController.getBaseUser(req.params.userslug, callerUID, function(err, userData) { + async.parallel({ + settings: function(next) { + user.getSettings(req.uid, next); + }, + userData: function(next) { + accountsController.getBaseUser(req.params.userslug, req.uid, next); + } + }, function(err, results) { if (err) { return next(err); } - + var userData = results.userData; if (!userData) { return helpers.notFound(req, res); } - method('uid:' + userData.uid + ':' + set, callerUID, 0, 19, function(err, data) { + var setName = 'uid:' + userData.uid + ':' + set; + + var page = Math.max(1, parseInt(req.query.page, 10) || 1); + var itemsPerPage = (tpl === 'account/topics' || tpl === 'account/watched') ? results.settings.topicsPerPage : results.settings.postsPerPage; + + async.parallel({ + itemCount: function(next) { + if (results.settings.usePagination) { + db.sortedSetCard(setName, next); + } else { + next(null, 0); + } + }, + data: function(next) { + var start = (page - 1) * itemsPerPage; + var stop = start + itemsPerPage; + method(setName, req.uid, start, stop, next); + } + }, function(err, results) { if (err) { return next(err); } - userData[type] = data[type]; - userData.nextStart = data.nextStart; + userData[type] = results.data[type]; + userData.nextStart = results.data.nextStart; + var pageCount = Math.ceil(results.itemCount / itemsPerPage); + + var pagination = require('../pagination'); + userData.pagination = pagination.create(page, pageCount); res.render(tpl, userData); }); @@ -317,11 +350,10 @@ accountsController.getBaseUser = function(userslug, callerUID, callback) { }; accountsController.accountEdit = function(req, res, next) { - var callerUID = req.user ? parseInt(req.user.uid, 10) : 0; var userData; async.waterfall([ function(next) { - getUserDataByUserSlug(req.params.userslug, callerUID, next); + getUserDataByUserSlug(req.params.userslug, req.uid, next); }, function(data, next) { userData = data; @@ -339,9 +371,7 @@ accountsController.accountEdit = function(req, res, next) { }; accountsController.accountSettings = function(req, res, next) { - var callerUID = req.user ? parseInt(req.user.uid, 10) : 0; - - accountsController.getBaseUser(req.params.userslug, callerUID, function(err, userData) { + accountsController.getBaseUser(req.params.userslug, req.uid, function(err, userData) { if (err) { return next(err); } @@ -378,32 +408,10 @@ accountsController.accountSettings = function(req, res, next) { accountsController.uploadPicture = function (req, res, next) { var userPhoto = req.files.files[0]; - var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; - var extension = path.extname(userPhoto.name); - var updateUid = req.user ? req.user.uid : 0; - var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; - var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; + + var updateUid = req.uid; async.waterfall([ - function(next) { - next(userPhoto.size > uploadSize * 1024 ? new Error('[[error:file-too-big, ' + uploadSize + ']]') : null); - }, - function(next) { - next(!extension ? new Error('[[error:invalid-image-extension]]') : null); - }, - function(next) { - file.isFileTypeAllowed(userPhoto.path, ['png', 'jpeg', 'jpg', 'gif'], next); - }, - function(next) { - image.resizeImage(userPhoto.path, extension, imageDimension, imageDimension, next); - }, - function(next) { - if (convertToPNG) { - image.convertImageToPng(userPhoto.path, extension, next); - } else { - next(); - } - }, function(next) { user.getUidByUserslug(req.params.userslug, next); }, @@ -412,7 +420,7 @@ accountsController.uploadPicture = function (req, res, next) { return next(); } - user.isAdministrator(req.user.uid, function(err, isAdmin) { + user.isAdministrator(req.uid, function(err, isAdmin) { if (err) { return next(err); } @@ -423,55 +431,22 @@ accountsController.uploadPicture = function (req, res, next) { updateUid = uid; next(); }); + }, + function(next) { + user.uploadPicture(updateUid, userPhoto, next); } - ], function(err, result) { - - function done(err, image) { - fs.unlink(userPhoto.path); - if (err) { - return res.json({error: err.message}); - } - - user.setUserFields(updateUid, {uploadedpicture: image.url, picture: image.url}); - - res.json([{name: userPhoto.name, url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url}]); - } - + ], function(err, image) { + fs.unlink(userPhoto.path); if (err) { - fs.unlink(userPhoto.path); return next(err); } - if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: userPhoto, uid: updateUid}, done); - } - - var filename = updateUid + '-profileimg' + (convertToPNG ? '.png' : extension); - - user.getUserField(updateUid, 'uploadedpicture', function (err, oldpicture) { - if (err) { - return next(err); - } - if (!oldpicture) { - file.saveFileToLocal(filename, 'profile', userPhoto.path, done); - return; - } - - var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), 'profile', path.basename(oldpicture)); - - fs.unlink(absolutePath, function (err) { - if (err) { - winston.error(err); - } - - file.saveFileToLocal(filename, 'profile', userPhoto.path, done); - }); - }); + res.json([{name: userPhoto.name, url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url}]); }); }; accountsController.getNotifications = function(req, res, next) { - user.notifications.getAll(req.user.uid, 40, function(err, notifications) { + user.notifications.getAll(req.uid, 40, function(err, notifications) { if (err) { return next(err); } diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 24332866f9..52cf194353 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -3,6 +3,7 @@ var async = require('async'), fs = require('fs'), path = require('path'), + nconf = require('nconf'), user = require('../user'), categories = require('../categories'), @@ -16,7 +17,6 @@ var async = require('async'), plugins = require('../plugins'), widgets = require('../widgets'), groups = require('../groups'), - pkg = require('../../package.json'), validator = require('validator'); @@ -33,6 +33,7 @@ var adminController = { events: {}, logs: {}, database: {}, + postCache: {}, plugins: {}, languages: {}, settings: {}, @@ -63,7 +64,7 @@ adminController.home = function(req, res, next) { return next(err); } res.render('admin/general/dashboard', { - version: pkg.version, + version: nconf.get('version'), notices: results.notices, stats: results.stats }); @@ -136,24 +137,38 @@ adminController.categories.get = function(req, res, next) { return next(err); } - res.render('admin/manage/category', { - category: data.category[0], - privileges: data.privileges + plugins.fireHook('filter:admin.category.get', {req: req, res: res, category: data.category[0], privileges: data.privileges}, function(err, data) { + if (err) { + return next(err); + } + + res.render('admin/manage/category', { + category: data.category, + privileges: data.privileges + }); }); }); }; adminController.categories.getAll = function(req, res, next) { - var uid = req.user ? parseInt(req.user.uid, 10) : 0, - active = [], + var active = [], disabled = []; - categories.getAllCategories(uid, function (err, categoryData) { + async.waterfall([ + function(next) { + db.getSortedSetRange('categories:cid', 0, -1, next); + }, + function(cids, next) { + categories.getCategoriesData(cids, next); + }, + function(categories, next) { + plugins.fireHook('filter:admin.categories.get', {req: req, res: res, categories: categories}, next); + } + ], function(err, data) { if (err) { return next(err); } - - categoryData.filter(Boolean).forEach(function(category) { + data.categories.filter(Boolean).forEach(function(category) { (category.disabled ? disabled : active).push(category); }); @@ -179,20 +194,20 @@ adminController.flags.get = function(req, res, next) { if (err) { return next(err); } - res.render('admin/manage/flags', {posts: posts, next: end + 1, byUsername: byUsername}); + res.render('admin/manage/flags', {posts: posts, next: stop + 1, byUsername: byUsername}); } - var uid = req.user ? parseInt(req.user.uid, 10) : 0; + var sortBy = req.query.sortBy || 'count'; var byUsername = req.query.byUsername || ''; var start = 0; - var end = 19; + var stop = 19; if (byUsername) { - posts.getUserFlags(byUsername, sortBy, uid, start, end, done); + posts.getUserFlags(byUsername, sortBy, req.uid, start, stop, done); } else { var set = sortBy === 'count' ? 'posts:flags:count' : 'posts:flagged'; - posts.getFlags(set, uid, start, end, done); - } + posts.getFlags(set, req.uid, start, stop, done); + } }; adminController.database.get = function(req, res, next) { @@ -222,6 +237,26 @@ adminController.logs.get = function(req, res, next) { }); }; +adminController.postCache.get = function(req, res, next) { + var cache = require('../posts/cache'); + var avgPostSize = 0; + var percentFull = 0; + if (cache.itemCount > 0) { + avgPostSize = parseInt((cache.length / cache.itemCount), 10); + percentFull = ((cache.length / cache.max) * 100).toFixed(2); + } + + res.render('admin/advanced/post-cache', { + cache: { + length: cache.length, + max: cache.max, + itemCount: cache.itemCount, + percentFull: percentFull, + avgPostSize: avgPostSize + } + }); +}; + adminController.plugins.get = function(req, res, next) { plugins.getAll(function(err, plugins) { if (err || !Array.isArray(plugins)) { @@ -236,6 +271,12 @@ adminController.plugins.get = function(req, res, next) { adminController.languages.get = function(req, res, next) { languages.list(function(err, languages) { + if (err) { + return next(err); + } + languages.forEach(function(language) { + language.selected = language.code === meta.config.defaultLang; + }); res.render('admin/general/languages', { languages: languages }); @@ -261,7 +302,7 @@ adminController.navigation.get = function(req, res, next) { if (err) { return next(err); } - + res.render('admin/general/navigation', data); }); }; @@ -374,7 +415,7 @@ adminController.extend.rewards = function(req, res, next) { if (err) { return next(err); } - + res.render('admin/extend/rewards', data); }); }; diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index 193b20b6d5..88509df0a0 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -2,6 +2,7 @@ var fs = require('fs'), path = require('path'), + nconf = require('nconf'), file = require('../../file'), plugins = require('../../plugins'); @@ -37,7 +38,7 @@ uploadsController.uploadFavicon = function(req, res, next) { return next(err); } - res.json([{name: uploadedFile.name, url: image.url}]); + res.json([{name: uploadedFile.name, url: nconf.get('relative_path') + image.url}]); }); } }; @@ -76,8 +77,7 @@ function uploadImage(filename, folder, uploadedFile, req, res, next) { if (err) { return next(err); } - - res.json([{name: uploadedFile.name, url: image.url}]); + res.json([{name: uploadedFile.name, url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url}]); } if (plugins.hasListeners('filter:uploadImage')) { diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 283302b48e..7f884b79ce 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -31,8 +31,7 @@ usersController.banned = function(req, res, next) { }; function getUsers(set, req, res, next) { - var uid = req.user ? parseInt(req.user.uid, 10) : 0; - user.getUsersFromSet(set, uid, 0, 49, function(err, users) { + user.getUsersFromSet(set, req.uid, 0, 49, function(err, users) { if (err) { return next(err); } @@ -45,7 +44,7 @@ function getUsers(set, req, res, next) { search_display: 'hidden', loadmore_display: 'block', users: users, - yourid: req.user.uid, + yourid: req.uid, requireEmailConfirmation: parseInt(meta.config.requireEmailConfirmation, 10) === 1 }); }); diff --git a/src/controllers/api.js b/src/controllers/api.js index 8be2211ca1..1af6e441fd 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -1,7 +1,6 @@ "use strict"; -var pkg = require('./../../package.json'), - meta = require('./../meta'), +var meta = require('./../meta'), user = require('./../user'), plugins = require('./../plugins'), widgets = require('../widgets'), @@ -26,7 +25,7 @@ apiController.getConfig = function(req, res, next) { config.relative_path = nconf.get('relative_path'); config.socketioTransports = nconf.get('socket.io:transports') || ['polling', 'websocket']; config.websocketAddress = nconf.get('socket.io:address') || ''; - config.version = pkg.version; + config.version = nconf.get('version'); config.siteTitle = validator.escape(meta.config.title || meta.config.browserTitle || 'NodeBB'); config.showSiteTitle = parseInt(meta.config.showSiteTitle, 10) === 1; config.postDelay = meta.config.postDelay; @@ -40,13 +39,16 @@ apiController.getConfig = function(req, res, next) { config.maximumUsernameLength = meta.config.maximumUsernameLength; config.minimumPasswordLength = meta.config.minimumPasswordLength; config.maximumSignatureLength = meta.config.maximumSignatureLength; + config.maximumAboutMeLength = meta.config.maximumAboutMeLength; config.useOutgoingLinksPage = parseInt(meta.config.useOutgoingLinksPage, 10) === 1; config.allowGuestSearching = parseInt(meta.config.allowGuestSearching, 10) === 1; config.allowGuestHandles = parseInt(meta.config.allowGuestHandles, 10) === 1; config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1; + config.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads) === 1; config.allowTopicsThumbnail = parseInt(meta.config.allowTopicsThumbnail, 10) === 1; config.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; config.privateUserInfo = parseInt(meta.config.privateUserInfo, 10) === 1; + config.privateTagListing = parseInt(meta.config.privateTagListing, 10) === 1; config.usePagination = parseInt(meta.config.usePagination, 10) === 1; config.disableSocialButtons = parseInt(meta.config.disableSocialButtons, 10) === 1; config.disableChat = parseInt(meta.config.disableChat, 10) === 1; @@ -98,7 +100,6 @@ apiController.getConfig = function(req, res, next) { apiController.renderWidgets = function(req, res, next) { var async = require('async'), - uid = req.user ? req.user.uid : 0, areas = { template: req.query.template, locations: req.query.locations, @@ -110,7 +111,7 @@ apiController.renderWidgets = function(req, res, next) { return res.status(200).json({}); } - widgets.render(uid, { + widgets.render(req.uid, { template: areas.template, url: areas.url, locations: areas.locations diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 55c0122705..80bf87907a 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -17,9 +17,8 @@ var categoriesController = {}, utils = require('../../public/src/utils'); categoriesController.recent = function(req, res, next) { - var uid = req.user ? req.user.uid : 0; - var end = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; - topics.getTopicsFromSet('topics:recent', uid, 0, end, function(err, data) { + var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; + topics.getTopicsFromSet('topics:recent', req.uid, 0, stop, function(err, data) { if (err) { return next(err); } @@ -34,7 +33,6 @@ categoriesController.recent = function(req, res, next) { var anonCache = {}, lastUpdateTime = 0; categoriesController.popular = function(req, res, next) { - var uid = req.user ? req.user.uid : 0; var terms = { daily: 'day', weekly: 'week', @@ -43,13 +41,13 @@ categoriesController.popular = function(req, res, next) { }; var term = terms[req.params.term] || 'day'; - if (uid === 0) { + if (!req.uid) { if (anonCache[term] && (Date.now() - lastUpdateTime) < 60 * 60 * 1000) { return res.render('popular', anonCache[term]); } } - topics.getPopular(term, uid, meta.config.topicsPerList, function(err, topics) { + topics.getPopular(term, req.uid, meta.config.topicsPerList, function(err, topics) { if (err) { return next(err); } @@ -61,7 +59,7 @@ categoriesController.popular = function(req, res, next) { breadcrumbs: helpers.buildBreadcrumbs([{text: '[[global:header.popular]]'}]) }; - if (uid === 0) { + if (!req.uid) { anonCache[term] = data; lastUpdateTime = Date.now(); } @@ -71,9 +69,8 @@ categoriesController.popular = function(req, res, next) { }; categoriesController.unread = function(req, res, next) { - var uid = req.user ? req.user.uid : 0; - var end = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; - topics.getUnreadTopics(uid, 0, end, function (err, data) { + var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; + topics.getUnreadTopics(req.uid, 0, stop, function (err, data) { if (err) { return next(err); } @@ -84,10 +81,8 @@ categoriesController.unread = function(req, res, next) { }; categoriesController.unreadTotal = function(req, res, next) { - var uid = req.user ? req.user.uid : 0; - - topics.getTotalUnread(uid, function (err, data) { - if(err) { + topics.getTotalUnread(req.uid, function (err, data) { + if (err) { return next(err); } @@ -122,11 +117,10 @@ categoriesController.list = function(req, res, next) { next(null); }, categories: function (next) { - var uid = req.user ? req.user.uid : 0; var categoryData; async.waterfall([ function(next) { - categories.getCategoriesByPrivilege(uid, 'find', next); + categories.getCategoriesByPrivilege(req.uid, 'find', next); }, function(_categoryData, next) { categoryData = _categoryData; @@ -143,7 +137,7 @@ categoriesController.list = function(req, res, next) { return category && !category.parent; }); - categories.getRecentTopicReplies(allCategories, uid, next); + categories.getRecentTopicReplies(allCategories, req.uid, next); } ], function(err) { next(err, categoryData); @@ -165,8 +159,7 @@ categoriesController.list = function(req, res, next) { categoriesController.get = function(req, res, next) { var cid = req.params.category_id, - page = req.query.page || 1, - uid = req.user ? req.user.uid : 0, + page = parseInt(req.query.page, 10) || 1, userPrivileges; if (req.params.topic_index && !utils.isNumber(req.params.topic_index)) { @@ -183,19 +176,17 @@ categoriesController.get = function(req, res, next) { categories.getCategoryFields(cid, ['slug', 'disabled', 'topic_count'], next); }, privileges: function(next) { - privileges.categories.get(cid, uid, next); + privileges.categories.get(cid, req.uid, next); }, userSettings: function(next) { - user.getSettings(uid, next); + user.getSettings(req.uid, next); } }, next); }, function(results, next) { - if (!results.exists || (results.categoryData && parseInt(results.categoryData.disabled, 10) === 1)) { - return helpers.notFound(req, res); - } + userPrivileges = results.privileges; - if (cid + '/' + req.params.slug !== results.categoryData.slug) { + if (!results.exists || (results.categoryData && parseInt(results.categoryData.disabled, 10) === 1)) { return helpers.notFound(req, res); } @@ -203,15 +194,22 @@ categoriesController.get = function(req, res, next) { return helpers.notAllowed(req, res); } + if ((!req.params.slug || results.categoryData.slug !== cid + '/' + req.params.slug) && (results.categoryData.slug && results.categoryData.slug !== cid + '/')) { + return helpers.redirect(res, '/category/' + encodeURI(results.categoryData.slug)); + } + + var settings = results.userSettings; var topicIndex = utils.isNumber(req.params.topic_index) ? parseInt(req.params.topic_index, 10) - 1 : 0; var topicCount = parseInt(results.categoryData.topic_count, 10); + var pageCount = Math.max(1, Math.ceil(topicCount / settings.topicsPerPage)); if (topicIndex < 0 || topicIndex > Math.max(topicCount - 1, 0)) { return helpers.redirect(res, '/category/' + cid + '/' + req.params.slug + (topicIndex > topicCount ? '/' + topicCount : '')); } - userPrivileges = results.privileges; - var settings = results.userSettings; + if (settings.usePagination && (page < 1 || page > pageCount)) { + return helpers.notFound(req, res); + } if (!settings.usePagination) { topicIndex = Math.max(topicIndex - (settings.topicsPerPage - 1), 0); @@ -232,15 +230,15 @@ categoriesController.get = function(req, res, next) { } var start = (page - 1) * settings.topicsPerPage + topicIndex, - end = start + settings.topicsPerPage - 1; + stop = start + settings.topicsPerPage - 1; next(null, { cid: cid, set: set, reverse: reverse, start: start, - end: end, - uid: uid + stop: stop, + uid: req.uid }); }, function(payload, next) { @@ -276,7 +274,7 @@ categoriesController.get = function(req, res, next) { }); }, function(categoryData, next) { - categories.getRecentTopicReplies(categoryData.children, uid, function(err) { + categories.getRecentTopicReplies(categoryData.children, req.uid, function(err) { next(err, categoryData); }); }, @@ -303,7 +301,7 @@ categoriesController.get = function(req, res, next) { } ]; - if(categoryData.backgroundImage) { + if (categoryData.backgroundImage) { res.locals.metaTags.push({ name: 'og:image', content: categoryData.backgroundImage diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 7371c924e7..e3bdd500a6 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -12,7 +12,7 @@ groupsController.list = function(req, res, next) { groups.list({ truncateUserList: true, expand: true, - uid: req.user ? parseInt(req.user.uid, 10) : 0 + uid: req.uid }, function(err, groups) { if (err) { return next(err); @@ -25,12 +25,12 @@ groupsController.list = function(req, res, next) { }; groupsController.details = function(req, res, next) { - var uid = req.user ? parseInt(req.user.uid, 10) : 0; - async.waterfall([ async.apply(groups.exists, res.locals.groupName), function(exists, next) { - if (!exists) { return next(undefined, null); } + if (!exists) { + return next(undefined, null); + } // Ensure the group isn't hidden either groups.isHidden(res.locals.groupName, next); @@ -43,8 +43,8 @@ groupsController.details = function(req, res, next) { } else { // If not, only members are granted access async.parallel([ - async.apply(groups.isMember, uid, res.locals.groupName), - async.apply(groups.isInvited, uid, res.locals.groupName) + async.apply(groups.isMember, req.uid, res.locals.groupName), + async.apply(groups.isInvited, req.uid, res.locals.groupName) ], function(err, checks) { next(err, checks[0] || checks[1]); }); @@ -63,11 +63,11 @@ groupsController.details = function(req, res, next) { group: function(next) { groups.get(res.locals.groupName, { expand: true, - uid: uid + uid: req.uid }, next); }, posts: function(next) { - groups.getLatestMemberPosts(res.locals.groupName, 10, uid, next); + groups.getLatestMemberPosts(res.locals.groupName, 10, req.uid, next); } }, function(err, results) { if (err) { @@ -84,13 +84,12 @@ groupsController.details = function(req, res, next) { }; groupsController.members = function(req, res, next) { - var uid = req.user ? parseInt(req.user.uid, 10) : 0; async.waterfall([ function(next) { groups.getGroupNameByGroupSlug(req.params.slug, next); }, function(groupName, next) { - user.getUsersFromSet('group:' + groupName + ':members', uid, 0, 49, next); + user.getUsersFromSet('group:' + groupName + ':members', req.uid, 0, 49, next); }, ], function(err, users) { if (err) { diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 0b100aad49..8d4ccb8895 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -26,13 +26,11 @@ helpers.notFound = function(req, res, error) { }; helpers.notAllowed = function(req, res, error) { - var uid = req.user ? req.user.uid : 0; - - if (uid) { + if (req.uid) { if (res.locals.isAPI) { - res.status(403).json({path: req.path.replace(/^\/api/, ''), loggedIn: !!uid, error: error}); + res.status(403).json({path: req.path.replace(/^\/api/, ''), loggedIn: !!req.uid, error: error}); } else { - res.status(403).render('403', {path: req.path, loggedIn: !!uid, error: error}); + res.status(403).render('403', {path: req.path, loggedIn: !!req.uid, error: error}); } } else { if (res.locals.isAPI) { diff --git a/src/controllers/index.js b/src/controllers/index.js index 74cce358e5..7a2f225290 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -57,9 +57,12 @@ Controllers.reset = function(req, res, next) { } res.render('reset_code', { valid: valid, + displayExpiryNotice: req.session.passwordExpired, code: req.params.code ? req.params.code : null, breadcrumbs: helpers.buildBreadcrumbs([{text: '[[reset_password:reset_password]]', url: '/reset'}, {text: '[[reset_password:update_password]]'}]) }); + + delete req.session.passwordExpired; }); } else { res.render('reset', { @@ -78,7 +81,7 @@ Controllers.login = function(req, res, next) { data.alternate_logins = loginStrategies.length > 0; data.authentication = loginStrategies; data.showResetLink = emailersPresent; - data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1; + data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1; data.allowRegistration = parseInt(meta.config.allowRegistration, 10) === 1; data.allowLoginWith = '[[login:' + (meta.config.allowLoginWith || 'username-email') + ']]'; data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:login]]'}]); @@ -118,7 +121,7 @@ Controllers.register = function(req, res, next) { data.error = req.flash('error')[0]; plugins.fireHook('filter:register.build', {req: req, res: res, templateData: data}, function(err, data) { - if (err && process.env === 'development') { + if (err && global.env === 'development') { winston.warn(JSON.stringify(err)); return next(err); } diff --git a/src/controllers/posts.js b/src/controllers/posts.js index e5a32e9791..a2bbfab226 100644 --- a/src/controllers/posts.js +++ b/src/controllers/posts.js @@ -1,17 +1,16 @@ "use strict"; var async = require('async'), - + posts = require('../posts'), privileges = require('../privileges'), helpers = require('./helpers'), postsController = {}; postsController.getPost = function(req, res, next) { - var uid = req.user ? parseInt(req.user.uid) : 0; async.parallel({ canRead: function(next) { - privileges.posts.can('read', req.params.pid, uid, next); + privileges.posts.can('read', req.params.pid, req.uid, next); }, postData: function(next) { posts.getPostData(req.params.pid, next); diff --git a/src/controllers/search.js b/src/controllers/search.js index 7c1d20262b..6637acbbc3 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -15,14 +15,17 @@ searchController.search = function(req, res, next) { return helpers.notFound(req, res); } - var uid = req.user ? req.user.uid : 0; var breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); - categories.getCategoriesByPrivilege(uid, 'read', function(err, categories) { + categories.getCategoriesByPrivilege(req.uid, 'read', function(err, categories) { if (err) { return next(err); } + categories = categories.filter(function(category) { + return category && !category.link; + }); + req.params.term = validator.escape(req.params.term); var page = Math.max(1, parseInt(req.query.page, 10)) || 1; if (req.query.categories && !Array.isArray(req.query.categories)) { @@ -42,7 +45,7 @@ searchController.search = function(req, res, next) { sortBy: req.query.sortBy, sortDirection: req.query.sortDirection, page: page, - uid: uid + uid: req.uid }; search.search(data, function(err, results) { @@ -50,8 +53,7 @@ searchController.search = function(req, res, next) { return next(err); } - var pageCount = Math.max(1, Math.ceil(results.matchCount / 10)); - results.pagination = pagination.create(page, pageCount, req.query); + results.pagination = pagination.create(page, results.pageCount, req.query); results.showAsPosts = !req.query.showAs || req.query.showAs === 'posts'; results.showAsTopics = req.query.showAs === 'topics'; results.breadcrumbs = breadcrumbs; diff --git a/src/controllers/tags.js b/src/controllers/tags.js index 3d450e06a7..c63e220cc8 100644 --- a/src/controllers/tags.js +++ b/src/controllers/tags.js @@ -5,51 +5,66 @@ var tagsController = {}, nconf = require('nconf'), validator = require('validator'), meta = require('../meta'), + user = require('../user'), topics = require('../topics'), helpers = require('./helpers'); tagsController.getTag = function(req, res, next) { var tag = validator.escape(req.params.tag); - var uid = req.user ? req.user.uid : 0; - var end = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; + var stop = (parseInt(meta.config.topicsPerList, 10) || 20) - 1; - topics.getTagTids(tag, 0, end, function(err, tids) { + async.waterfall([ + function(next) { + topics.getTagTids(tag, 0, stop, next); + }, + function(tids, next) { + if (Array.isArray(tids) && !tids.length) { + topics.deleteTag(tag); + return res.render('tag', { + topics: [], + tag: tag, + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[tags:tags]]', url: '/tags'}, {text: tag}]) + }); + } + + async.parallel({ + isAdmin: async.apply(user.isAdministrator, req.uid), + topics: async.apply(topics.getTopics, tids, req.uid) + }, next); + } + ], function(err, results) { if (err) { return next(err); } - if (Array.isArray(tids) && !tids.length) { - topics.deleteTag(tag); - return res.render('tag', {topics: [], tag: tag}); + if (!results.isAdmin) { + results.topics = results.topics.filter(function(topic) { + return topic && !topic.deleted; + }); } - topics.getTopics(tids, uid, function(err, topics) { - if (err) { - return next(err); + res.locals.metaTags = [ + { + name: 'title', + content: tag + }, + { + property: 'og:title', + content: tag + }, + { + property: 'og:url', + content: nconf.get('url') + '/tags/' + tag } + ]; - res.locals.metaTags = [ - { - name: 'title', - content: tag - }, - { - property: 'og:title', - content: tag - }, - { - property: 'og:url', - content: nconf.get('url') + '/tags/' + tag - } - ]; - var data = { - topics: topics, - tag: tag, - nextStart: end + 1, - breadcrumbs: helpers.buildBreadcrumbs([{text: '[[tags:tags]]', url: '/tags'}, {text: tag}]) - }; - res.render('tag', data); - }); + var data = { + topics: results.topics, + tag: tag, + nextStart: stop + 1, + breadcrumbs: helpers.buildBreadcrumbs([{text: '[[tags:tags]]', url: '/tags'}, {text: tag}]) + }; + res.render('tag', data); }); }; diff --git a/src/controllers/topics.js b/src/controllers/topics.js index 8429e19396..3f96682c31 100644 --- a/src/controllers/topics.js +++ b/src/controllers/topics.js @@ -18,9 +18,7 @@ var topicsController = {}, topicsController.get = function(req, res, next) { var tid = req.params.topic_id, - page = 1, sort = req.query.sort, - uid = req.user ? req.user.uid : 0, userPrivileges; if (req.params.post_index && !utils.isNumber(req.params.post_index)) { @@ -31,10 +29,10 @@ topicsController.get = function(req, res, next) { function (next) { async.parallel({ privileges: function(next) { - privileges.topics.get(tid, uid, next); + privileges.topics.get(tid, req.uid, next); }, settings: function(next) { - user.getSettings(uid, next); + user.getSettings(req.uid, next); }, topic: function(next) { topics.getTopicFields(tid, ['slug', 'postcount', 'deleted'], next); @@ -44,23 +42,24 @@ topicsController.get = function(req, res, next) { function (results, next) { userPrivileges = results.privileges; - if (userPrivileges.disabled || tid + '/' + req.params.slug !== results.topic.slug) { - return helpers.notFound(req, res); - } - if (!userPrivileges.read || (parseInt(results.topic.deleted, 10) && !userPrivileges.view_deleted)) { return helpers.notAllowed(req, res); } + if ((!req.params.slug || results.topic.slug !== tid + '/' + req.params.slug) && (results.topic.slug && results.topic.slug !== tid + '/')) { + return helpers.redirect(res, '/topic/' + encodeURI(results.topic.slug)); + } + var settings = results.settings; var postCount = parseInt(results.topic.postcount, 10); var pageCount = Math.max(1, Math.ceil((postCount - 1) / settings.postsPerPage)); + var page = parseInt(req.query.page, 10) || 1; if (utils.isNumber(req.params.post_index) && (req.params.post_index < 1 || req.params.post_index > postCount)) { return helpers.redirect(res, '/topic/' + req.params.topic_id + '/' + req.params.slug + (req.params.post_index > postCount ? '/' + postCount : '')); } - if (settings.usePagination && (req.query.page < 1 || req.query.page > pageCount)) { + if (settings.usePagination && (page < 1 || page > pageCount)) { return helpers.notFound(req, res); } @@ -83,16 +82,16 @@ topicsController.get = function(req, res, next) { } var postIndex = 0; - page = parseInt(req.query.page, 10) || 1; + req.params.post_index = parseInt(req.params.post_index, 10) || 0; if (reverse && req.params.post_index === 1) { req.params.post_index = 0; } if (!settings.usePagination) { if (reverse) { - postIndex = Math.max(0, postCount - (req.params.post_index || postCount) - (settings.postsPerPage - 1)); + postIndex = Math.max(0, postCount - (req.params.post_index || postCount) - (settings.postsPerPage / 2)); } else { - postIndex = Math.max(0, (req.params.post_index || 1) - (settings.postsPerPage + 1)); + postIndex = Math.max(0, (req.params.post_index || 1) - (settings.postsPerPage / 2)); } } else if (!req.query.page) { var index = 0; @@ -106,9 +105,9 @@ topicsController.get = function(req, res, next) { } var start = (page - 1) * settings.postsPerPage + postIndex, - end = start + settings.postsPerPage - 1; + stop = start + settings.postsPerPage - 1; - topics.getTopicWithPosts(tid, set, uid, start, end, reverse, function (err, topicData) { + topics.getTopicWithPosts(tid, set, req.uid, start, stop, reverse, function (err, topicData) { if (err && err.message === '[[error:no-topic]]' && !topicData) { return helpers.notFound(req, res); } @@ -255,7 +254,7 @@ topicsController.get = function(req, res, next) { 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['rssFeedUrl'] = nconf.get('relative_path') + '/topic/' + data.tid + '.rss'; + data.rssFeedUrl = nconf.get('relative_path') + '/topic/' + data.tid + '.rss'; data.pagination = pagination.create(data.currentPage, data.pageCount); data.pagination.rel.forEach(function(rel) { res.locals.linkTags.push(rel); @@ -274,42 +273,36 @@ topicsController.get = function(req, res, next) { topicsController.teaser = function(req, res, next) { var tid = req.params.topic_id; - var uid = req.user ? parseInt(req.user.uid, 10) : 0; if (!utils.isNumber(tid)) { return next(new Error('[[error:invalid-tid]]')); } - privileges.topics.can('read', tid, uid, function(err, canRead) { - if (err) { - return next(err); - } - - if (!canRead) { - return res.status(403).json('[[error:no-privileges]]'); - } - - topics.getLatestUndeletedPid(tid, function(err, pid) { - if (err) { - return next(err); + async.waterfall([ + function(next) { + privileges.topics.can('read', tid, req.uid, next); + }, + function(canRead, next) { + if (!canRead) { + return res.status(403).json('[[error:no-privileges]]'); } - + topics.getLatestUndeletedPid(tid, next); + }, + function(pid, next) { if (!pid) { return res.status(404).json('not-found'); } + posts.getPostSummaryByPids([pid], req.uid, {stripTags: false}, next); + } + ], function(err, posts) { + if (err) { + return next(err); + } - posts.getPostSummaryByPids([pid], uid, {stripTags: false}, function(err, posts) { - if (err) { - return next(err); - } - - if (!Array.isArray(posts) || !posts.length) { - return res.status(404).json('not-found'); - } - - res.json(posts[0]); - }); - }); + if (!Array.isArray(posts) || !posts.length) { + return res.status(404).json('not-found'); + } + res.json(posts[0]); }); }; diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 79ae6fae19..1c99026463 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -5,6 +5,7 @@ var uploadsController = {}, fs = require('fs'), path = require('path'), async = require('async'), + nconf = require('nconf'), validator = require('validator'), meta = require('../meta'), @@ -118,7 +119,7 @@ function uploadFile(uid, uploadedFile, callback) { if (uploadedFile.size > parseInt(meta.config.maximumFileSize, 10) * 1024) { return callback(new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]')); } - + var filename = uploadedFile.name || 'upload'; filename = Date.now() + '-' + validator.escape(filename).substr(0, 255); @@ -128,7 +129,7 @@ function uploadFile(uid, uploadedFile, callback) { } callback(null, { - url: upload.url, + url: nconf.get('relative_path') + upload.url, name: uploadedFile.name }); }); diff --git a/src/controllers/users.js b/src/controllers/users.js index a4cb36259b..7b0047b7ad 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -7,22 +7,22 @@ var async = require('async'), meta = require('../meta'), pagination = require('../pagination'), plugins = require('../plugins'), - db = require('../database'); + db = require('../database'), + helpers = require('./helpers'); usersController.getOnlineUsers = function(req, res, next) { var websockets = require('../socket.io'); - var uid = req.user ? req.user.uid : 0; async.parallel({ users: function(next) { - user.getUsersFromSet('users:online', uid, 0, 49, next); + user.getUsersFromSet('users:online', req.uid, 0, 49, next); }, count: function(next) { var now = Date.now(); db.sortedSetCount('users:online', now - 300000, now, next); }, isAdministrator: function(next) { - user.isAdministrator(uid, next); + user.isAdministrator(req.uid, next); } }, function(err, results) { if (err) { @@ -35,14 +35,12 @@ usersController.getOnlineUsers = function(req, res, next) { }); } - var anonymousUserCount = websockets.getOnlineAnonCount(); - var userData = { + 'route_users:online': true, search_display: 'hidden', loadmore_display: results.count > 50 ? 'block' : 'hide', users: results.users, - anonymousUserCount: anonymousUserCount, - show_anon: anonymousUserCount ? '' : 'hide' + anonymousUserCount: websockets.getOnlineAnonCount() }; res.render('users', userData); @@ -62,9 +60,7 @@ usersController.getUsersSortedByJoinDate = function(req, res, next) { }; usersController.getUsers = function(set, count, req, res, next) { - var uid = req.user ? req.user.uid : 0; - - getUsersAndCount(set, uid, count, function(err, data) { + getUsersAndCount(set, req.uid, count, function(err, data) { if (err) { return next(err); } @@ -73,10 +69,9 @@ usersController.getUsers = function(set, count, req, res, next) { search_display: 'hidden', loadmore_display: data.count > count ? 'block' : 'hide', users: data.users, - show_anon: 'hide', pagination: pagination.create(1, pageCount) }; - + userData['route_' + set] = true; res.render('users', userData); }); }; @@ -102,10 +97,12 @@ function getUsersAndCount(set, uid, count, callback) { } usersController.getUsersForSearch = function(req, res, next) { - var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20, - uid = req.user ? req.user.uid : 0; + if (!req.uid) { + return helpers.notAllowed(req, res); + } + var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20; - getUsersAndCount('users:joindate', uid, resultsPerPage, function(err, data) { + getUsersAndCount('users:joindate', req.uid, resultsPerPage, function(err, data) { if (err) { return next(err); } @@ -113,8 +110,7 @@ usersController.getUsersForSearch = function(req, res, next) { var userData = { search_display: 'block', loadmore_display: 'hidden', - users: data.users, - show_anon: 'hide' + users: data.users }; res.render('users', userData); diff --git a/src/database/mongo.js b/src/database/mongo.js index e420f003b9..e031132b99 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -22,12 +22,14 @@ }, { name: 'mongo:username', - description: 'MongoDB username' + description: 'MongoDB username', + 'default': nconf.get('mongo:username') || '' }, { name: 'mongo:password', description: 'Password of your MongoDB database', - hidden: true + hidden: true, + before: function(value) { value = value || nconf.get('mongo:password') || ''; return value; } }, { name: "mongo:database", @@ -40,6 +42,7 @@ module.helpers.mongo = require('./mongo/helpers'); module.init = function(callback) { + callback = callback || function() {}; try { var sessionStore; mongoClient = require('mongodb').MongoClient; @@ -70,7 +73,16 @@ nconf.set('mongo:database', '0'); } - var connString = 'mongodb://' + usernamePassword + nconf.get('mongo:host') + ':' + nconf.get('mongo:port') + '/' + nconf.get('mongo:database'); + var hosts = nconf.get('mongo:host').split(','); + var ports = nconf.get('mongo:port').toString().split(','); + var servers = []; + + for (var i = 0; i < hosts.length; i++) { + servers.push(hosts[i] + ':' + ports[i]); + } + + var connString = 'mongodb://' + usernamePassword + servers.join() + '/' + nconf.get('mongo:database'); + var connOptions = { server: { poolSize: parseInt(nconf.get('mongo:poolSize'), 10) || 10 @@ -85,10 +97,10 @@ db = _db; module.client = db; - + if (!nconf.get('redis')) { // TEMP: to fix connect-mongo, see https://github.com/kcbanner/connect-mongo/issues/161 - db.openCalled = true + db.openCalled = true; module.sessionStore = new sessionStore({ db: db }); @@ -119,29 +131,28 @@ } function createIndices() { - createIndex('objects', {_key: 1, score: -1}, {background:true}); - createIndex('objects', {_key: 1, value: -1}, {background:true}); - createIndex('objects', {expireAt: 1}, {expireAfterSeconds:0, background:true}); + async.parallel([ + async.apply(createIndex, 'objects', {_key: 1, score: -1}, {background: true}), + async.apply(createIndex, 'objects', {_key: 1, value: -1}, {background: true}), + async.apply(createIndex, 'objects', {expireAt: 1}, {expireAfterSeconds: 0, background: true}), - createIndex('searchtopic', {content: 'text', uid: 1, cid: 1}, {background:true}); - createIndex('searchtopic', {id: 1}, {background:true}); + async.apply(createIndex, 'searchtopic', {content: 'text', uid: 1, cid: 1}, {background: true}), + async.apply(createIndex, 'searchtopic', {id: 1}, {background: true}), - - createIndex('searchpost', {content: 'text', uid: 1, cid: 1}, {background:true}); - createIndex('searchpost', {id: 1}, {background:true}); - - - if (typeof callback === 'function') { - callback(); - } + async.apply(createIndex, 'searchpost', {content: 'text', uid: 1, cid: 1}, {background: true}), + async.apply(createIndex, 'searchpost', {id: 1}, {background: true}) + ], function(err) { + callback(err); + }); } - function createIndex(collection, index, options) { + function createIndex(collection, index, options, callback) { db.collection(collection).ensureIndex(index, options, function(err) { if (err) { winston.error('Error creating index ' + err.message); } + callback(err); }); } }); @@ -152,4 +163,3 @@ }; }(exports)); - diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index a056de991f..1d8f992ab9 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -377,8 +377,12 @@ module.exports = function(db, module) { }; module.isSortedSetMember = function(key, value, callback) { - module.sortedSetScore(key, value, function(err, score) { - callback(err, !!score); + if (!key) { + return callback(); + } + value = helpers.valueToString(value); + db.collection('objects').findOne({_key: key, value: value}, {_id: 0, value: 1}, function(err, result) { + callback(err, !!result); }); }; diff --git a/src/database/redis.js b/src/database/redis.js index 716a8fd715..8e06ce837a 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -28,7 +28,8 @@ { name: 'redis:password', description: 'Password of your Redis database', - hidden: true + hidden: true, + before: function(value) { value = value || nconf.get('redis:password') || ''; return value; } }, { name: "redis:database", diff --git a/src/database/redis/main.js b/src/database/redis/main.js index b09574e10f..84e31eaae4 100644 --- a/src/database/redis/main.js +++ b/src/database/redis/main.js @@ -105,18 +105,30 @@ module.exports = function(redisClient, module) { }; module.expire = function(key, seconds, callback) { - redisClient.expire(key, seconds, callback); + callback = callback || function() {}; + redisClient.expire(key, seconds, function(err) { + callback(err); + }); }; module.expireAt = function(key, timestamp, callback) { - redisClient.expireat(key, timestamp, callback); + callback = callback || function() {}; + redisClient.expireat(key, timestamp, function(err) { + callback(err); + }); }; module.pexpire = function(key, ms, callback) { - redisClient.pexpire(key, ms, callback); + callback = callback || function() {}; + redisClient.pexpire(key, ms, function(err) { + callback(err); + }); }; module.pexpireAt = function(key, timestamp, callback) { - redisClient.pexpireat(key, timestamp, callback); + callback = callback || function() {}; + redisClient.pexpireat(key, timestamp, function(err) { + callback(err); + }); }; }; diff --git a/src/emitter.js b/src/emitter.js index 4d9b0f0164..468f604aaf 100644 --- a/src/emitter.js +++ b/src/emitter.js @@ -6,39 +6,30 @@ var eventEmitter = new (require('events')).EventEmitter(); eventEmitter.all = function(events, callback) { var eventList = events.slice(0); - function onEvent(event) { - eventEmitter.on(events[event], function() { - eventList.splice(eventList.indexOf(events[event]), 1); - + events.forEach(function onEvent(event) { + eventEmitter.on(event, function() { + var index = eventList.indexOf(event); + if (index === -1) { + return; + } + eventList.splice(index, 1); if (eventList.length === 0) { callback(); } }); - } - - for (var ev in events) { - if (events.hasOwnProperty(ev)) { - onEvent(ev); - } - } + }); }; eventEmitter.any = function(events, callback) { - function onEvent(event) { - eventEmitter.on(events[event], function() { + events.forEach(function onEvent(event) { + eventEmitter.on(event, function() { if (events !== null) { callback(); } events = null; }); - } - - for (var ev in events) { - if (events.hasOwnProperty(ev)) { - onEvent(ev); - } - } + }); }; module.exports = eventEmitter; \ No newline at end of file diff --git a/src/groups.js b/src/groups.js index c2257c9935..dd5ded7dab 100644 --- a/src/groups.js +++ b/src/groups.js @@ -93,8 +93,8 @@ var async = require('async'), }); }; - Groups.getGroups = function(start, end, callback) { - db.getSortedSetRevRange('groups:createtime', start, end, callback); + Groups.getGroups = function(start, stop, callback) { + db.getSortedSetRevRange('groups:createtime', start, stop, callback); }; Groups.get = function(groupName, options, callback) { @@ -115,6 +115,10 @@ var async = require('async'), return next(err); } + uids = uids.filter(function(uid) { + return uid && parseInt(uid, 10); + }); + if (options.truncateUserList) { var userListCount = parseInt(options.userListCount, 10) || 4; if (uids.length > userListCount) { @@ -315,8 +319,8 @@ var async = require('async'), }); }; - Groups.getMembers = function(groupName, start, end, callback) { - db.getSortedSetRevRange('group:' + groupName + ':members', start, end, callback); + Groups.getMembers = function(groupName, start, stop, callback) { + db.getSortedSetRevRange('group:' + groupName + ':members', start, stop, callback); }; Groups.getMembersOfGroups = function(groupNames, callback) { @@ -530,6 +534,8 @@ var async = require('async'), if (data.hasOwnProperty('ownerUid')) { tasks.push(async.apply(db.setAdd, 'group:' + data.name + ':owners', data.ownerUid)); tasks.push(async.apply(db.sortedSetAdd, 'group:' + data.name + ':members', now, data.ownerUid)); + + groupData.ownerUid = data.ownerUid; } if (!data.hidden) { @@ -541,7 +547,7 @@ var async = require('async'), plugins.fireHook('action:group.create', groupData); } - callback(err); + callback(err, groupData); }); }); }; diff --git a/src/install.js b/src/install.js index be1c322267..8b7c97ffc5 100644 --- a/src/install.js +++ b/src/install.js @@ -47,15 +47,18 @@ questions.main = [ questions.optional = [ { name: 'port', - default: 4567 + default: nconf.get('port') || 4567 } ]; function checkSetupFlag(next) { - var setupVal; + var envSetupKeys = ['database'], + setupVal; try { - setupVal = JSON.parse(nconf.get('setup')); - } catch (e) { + if (nconf.get('setup')) { + setupVal = JSON.parse(nconf.get('setup')); + } + } catch (err) { setupVal = undefined; } @@ -80,6 +83,15 @@ function checkSetupFlag(next) { process.exit(); } + } else if (envSetupKeys.every(function(key) { + return nconf.stores.env.store.hasOwnProperty(key); + })) { + install.values = envSetupKeys.reduce(function(config, key) { + config[key] = nconf.stores.env.store[key]; + return config; + }, {}); + + next(); } else { next(); } @@ -223,7 +235,7 @@ install.installDbDependencies = function(server_conf, next) { }; function setupDefaultConfigs(next) { - winston.info('Populating database with default configs, if not already set...'); + process.stdout.write('Populating database with default configs, if not already set...\n'); var meta = require('./meta'), defaults = require(path.join(__dirname, '../', 'install/data/defaults.json')); @@ -253,11 +265,11 @@ function enableDefaultTheme(next) { meta.configs.get('theme:id', function(err, id) { if (err || id) { - winston.info('Previous theme detected, skipping enabling default theme'); + process.stdout.write('Previous theme detected, skipping enabling default theme\n'); return next(err); } - winston.info('Enabling default theme: Lavender'); + process.stdout.write('Enabling default theme: Lavender\n'); meta.themes.set({ type: 'local', id: 'nodebb-theme-lavender' @@ -269,7 +281,7 @@ function createAdministrator(next) { var Groups = require('./groups'); Groups.get('administrators', {}, function (err, groupObj) { if (!err && groupObj && groupObj.memberCount > 0) { - winston.info('Administrator found, skipping Admin setup'); + process.stdout.write('Administrator found, skipping Admin setup\n'); next(); } else { createAdmin(next); @@ -279,9 +291,10 @@ function createAdministrator(next) { function createAdmin(callback) { var User = require('./user'), - Groups = require('./groups'); + Groups = require('./groups'), + password; - winston.warn('No administrators have been detected, running initial user setup'); + winston.warn('No administrators have been detected, running initial user setup\n'); var questions = [{ name: 'username', @@ -323,7 +336,9 @@ function createAdmin(callback) { return callback(new Error('invalid-values')); } - Groups.join('administrators', uid, callback); + Groups.join('administrators', uid, function(err) { + callback(err, password ? results : undefined); + }); }); }, retryPassword = function (originalResults) { @@ -348,11 +363,17 @@ function createAdmin(callback) { if (!install.values) { prompt.get(questions, success); } else { + // If automated setup did not provide a user password, generate one, it will be shown to the user upon setup completion + if (!install.values.hasOwnProperty('admin:password') && !nconf.get('admin:password')) { + process.stdout.write('Password was not provided during automated setup, generating one...\n'); + password = utils.generateUUID().slice(0, 8); + } + var results = { - username: install.values['admin:username'], - email: install.values['admin:email'], - password: install.values['admin:password'], - 'password:confirm': install.values['admin:password:confirm'] + username: install.values['admin:username'] || nconf.get('admin:username') || 'admin', + email: install.values['admin:email'] || nconf.get('admin:email') || '', + password: install.values['admin:password'] || nconf.get('admin:password') || password, + 'password:confirm': install.values['admin:password:confirm'] || nconf.get('admin:password') || password }; success(null, results); @@ -368,11 +389,11 @@ function createCategories(next) { } if (Array.isArray(categoryData) && categoryData.length) { - winston.info('Categories OK. Found ' + categoryData.length + ' categories.'); + process.stdout.write('Categories OK. Found ' + categoryData.length + ' categories.\n'); return next(); } - winston.warn('No categories found, populating instance with default categories'); + process.stdout.write('No categories found, populating instance with default categories\n'); fs.readFile(path.join(__dirname, '../', 'install/data/categories.json'), function (err, default_categories) { if (err) { @@ -423,7 +444,7 @@ function createWelcomePost(next) { function enableDefaultPlugins(next) { var Plugins = require('./plugins'); - winston.info('Enabling default plugins'); + process.stdout.write('Enabling default plugins\n'); var defaultEnabled = [ 'nodebb-plugin-markdown', @@ -452,16 +473,18 @@ function setCopyrightWidget(next) { if (err) { return next(err); } - + if (!results.footer && results.footerJSON) { - db.setObjectField('widgets:global', 'footer', results.footerJSON.toString(), next); + db.setObjectField('widgets:global', 'footer', results.footerJSON.toString(), next); } else { next(); } - }); + }); } install.setup = function (callback) { + + async.series([ checkSetupFlag, checkCIFlag, @@ -475,14 +498,27 @@ install.setup = function (callback) { enableDefaultPlugins, setCopyrightWidget, function (next) { - require('./upgrade').upgrade(next); + var upgrade = require('./upgrade'); + upgrade.check(function(err, uptodate) { + if (err) { + return next(err); + } + if (!uptodate) { upgrade.upgrade(next); } + else { next(); } + }); } - ], function (err) { + ], function (err, results) { if (err) { winston.warn('NodeBB Setup Aborted.\n ' + err.stack); process.exit(); } else { - callback(); + var data = {}; + if (results[6]) { + data.username = results[6].username; + data.password = results[6].password; + } + + callback(null, data); } }); }; @@ -500,7 +536,7 @@ install.save = function (server_conf, callback) { return callback(err); } - winston.info('Configuration Saved OK'); + process.stdout.write('Configuration Saved OK\n'); nconf.file({ file: path.join(__dirname, '..', 'config.json') diff --git a/src/messaging.js b/src/messaging.js index 4a367fbd38..cd3e1f96e2 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -10,7 +10,8 @@ var db = require('./database'), utils = require('../public/src/utils'), notifications = require('./notifications'), userNotifications = require('./user/notifications'), - emailer = require('./emailer'); + emailer = require('./emailer'), + sockets = require('./socket.io'); (function(Messaging) { Messaging.notifyQueue = {}; // Only used to notify a user of a new chat message, see Messaging.notifyUser @@ -26,14 +27,20 @@ var db = require('./database'), return [fromuid, touid].sort(); } - Messaging.addMessage = function(fromuid, touid, content, callback) { + Messaging.addMessage = function(fromuid, touid, content, timestamp, callback) { var uids = sortUids(fromuid, touid); + if (typeof timestamp === 'function') { + callback = timestamp; + timestamp = Date.now(); + } else { + timestamp = timestamp || Date.now(); + } + db.incrObjectField('global', 'nextMid', function(err, mid) { if (err) { return callback(err); } - var timestamp = Date.now(); var message = { content: content, timestamp: timestamp, @@ -225,10 +232,8 @@ var db = require('./database'), db.sortedSetAdd('uid:' + uid + ':chats', Date.now(), toUid, callback); }; - Messaging.getRecentChats = function(uid, start, end, callback) { - var websockets = require('./socket.io'); - - db.getSortedSetRevRange('uid:' + uid + ':chats', start, end, function(err, uids) { + Messaging.getRecentChats = function(uid, start, stop, callback) { + db.getSortedSetRevRange('uid:' + uid + ':chats', start, stop, function(err, uids) { if (err) { return callback(err); } @@ -245,22 +250,28 @@ var db = require('./database'), return callback(err); } + results.users.forEach(function(user, index) { + if (user && !parseInt(user.uid, 10)) { + Messaging.markRead(uid, uids[index]); + } + }); + results.users = results.users.filter(function(user) { return user && parseInt(user.uid, 10); }); if (!results.users.length) { - return callback(null, {users: [], nextStart: end + 1}); + return callback(null, {users: [], nextStart: stop + 1}); } results.users.forEach(function(user, index) { if (user) { user.unread = results.unread[index]; - user.status = require('./socket.io').isUserOnline(user.uid) ? user.status : 'offline'; + user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline'; } }); - callback(null, {users: results.users, nextStart: end + 1}); + callback(null, {users: results.users, nextStart: stop + 1}); }); }); }; @@ -269,6 +280,15 @@ var db = require('./database'), db.sortedSetCard('uid:' + uid + ':chats:unread', callback); }; + Messaging.pushUnreadCount = function(uid) { + Messaging.getUnreadCount(uid, function(err, unreadCount) { + if (err) { + return; + } + sockets.in('uid_' + uid).emit('event:unread.updateChatCount', null, unreadCount); + }); + }; + Messaging.markRead = function(uid, toUid, callback) { db.sortedSetRemove('uid:' + uid + ':chats:unread', toUid, callback); }; @@ -278,6 +298,23 @@ var db = require('./database'), }; Messaging.notifyUser = function(fromuid, touid, messageObj) { + // Immediate notifications + // Recipient + Messaging.pushUnreadCount(touid); + sockets.in('uid_' + touid).emit('event:chats.receive', { + withUid: fromuid, + message: messageObj, + self: 0 + }); + // Sender + Messaging.pushUnreadCount(fromuid); + sockets.in('uid_' + fromuid).emit('event:chats.receive', { + withUid: touid, + message: messageObj, + self: 1 + }); + + // Delayed notifications var queueObj = Messaging.notifyQueue[fromuid + ':' + touid]; if (queueObj) { queueObj.message.content += '\n' + messageObj.content; @@ -298,7 +335,32 @@ var db = require('./database'), }; Messaging.canMessage = function(fromUid, toUid, callback) { + if (parseInt(meta.config.disableChat) === 1) { + return callback(new Error('[[error:chat-disabled]]')); + } else if (toUid === fromUid) { + return callback(new Error('[[error:cant-chat-with-yourself]]')); + } else if (fromUid === 0) { + return callback(new Error('[[error:not-logged-in]]')); + } + async.waterfall([ + function(next) { + user.getUserFields(fromUid, ['banned', 'email:confirmed'], function(err, userData) { + if (err) { + return callback(err); + } + + if (parseInt(userData.banned, 10) === 1) { + return callback(new Error('[[error:user-banned]]')); + } + + if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && parseInt(userData['email:confirmed'], 10) !== 1) { + return callback(new Error('[[error:email-not-confirmed-chat]]')); + } + + next(); + }); + }, function(next) { user.getSettings(toUid, next); }, @@ -319,7 +381,7 @@ var db = require('./database'), }; function sendNotifications(fromuid, touid, messageObj, callback) { - if (require('./socket.io').isUserOnline(touid)) { + if (sockets.isUserOnline(touid)) { return callback(); } diff --git a/src/meta.js b/src/meta.js index 4aa65f6719..7226de3c60 100644 --- a/src/meta.js +++ b/src/meta.js @@ -23,6 +23,7 @@ var async = require('async'), require('./meta/sounds')(Meta); require('./meta/settings')(Meta); require('./meta/logs')(Meta); + require('./meta/tags')(Meta); Meta.templates = require('./meta/templates'); /* Assorted */ @@ -53,6 +54,7 @@ var async = require('async'), async.series([ async.apply(plugins.clearRequireCache), async.apply(plugins.reload), + async.apply(plugins.reloadRoutes), function(next) { async.parallel([ async.apply(Meta.js.minify, false), diff --git a/src/meta/configs.js b/src/meta/configs.js index e6d97f211b..65ffb45b11 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -4,7 +4,7 @@ var winston = require('winston'), db = require('../database'), pubsub = require('../pubsub'), - pkg = require('../../package.json'); + nconf = require('nconf'); module.exports = function(Meta) { @@ -28,7 +28,7 @@ module.exports = function(Meta) { Meta.configs.list = function (callback) { db.getObject('config', function (err, config) { config = config || {}; - config.version = pkg.version; + config.version = nconf.get('version'); callback(err, config); }); }; diff --git a/src/meta/tags.js b/src/meta/tags.js new file mode 100644 index 0000000000..022a3ef778 --- /dev/null +++ b/src/meta/tags.js @@ -0,0 +1,60 @@ +var nconf = require('nconf'), + validator = require('validator'), + async = require('async'), + plugins = require('../plugins'); + +module.exports = function(Meta) { + Meta.tags = {}; + + Meta.tags.parse = function(meta, link, callback) { + async.parallel([ + async.apply(plugins.fireHook, 'filter:meta.getMetaTags', [{ + name: 'viewport', + content: 'width=device-width, initial-scale=1.0, user-scalable=no' + }, { + name: 'content-type', + content: 'text/html; charset=UTF-8' + }, { + name: 'apple-mobile-web-app-capable', + content: 'yes' + }, { + property: 'og:site_name', + content: Meta.config.title || 'NodeBB' + }, { + name: 'keywords', + content: Meta.config.keywords || '' + }, { + name: 'msapplication-badge', + content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml' + }, { + name: 'msapplication-square150x150logo', + content: Meta.config['brand:logo'] || '' + }]), + async.apply(plugins.fireHook, 'filter:meta.getLinkTags', [{ + rel: "icon", + type: "image/x-icon", + href: nconf.get('relative_path') + '/favicon.ico' + }, { + rel: 'apple-touch-icon', + href: nconf.get('relative_path') + '/apple-touch-icon' + }]) + ], function(err, tags) { + meta = tags[0].concat(meta || []).map(function(tag) { + if(!tag || typeof tag.content !== 'string') { + winston.warn('Invalid meta tag. ', tag); + return tag; + } + + tag.content = validator.escape(tag.content); + return tag; + }); + + link = tags[1].concat(link || []); + + callback(null, { + meta: meta, + link: link + }); + }); + }; +}; \ No newline at end of file diff --git a/src/meta/templates.js b/src/meta/templates.js index 78596d59a4..35bb80f06e 100644 --- a/src/meta/templates.js +++ b/src/meta/templates.js @@ -15,6 +15,7 @@ var mkdirp = require('mkdirp'), Templates = {}; Templates.compile = function(callback) { + callback = callback || function() {}; var fromFile = nconf.get('from-file') || ''; if (nconf.get('isPrimary') === 'false' || fromFile.match('tpl')) { @@ -22,11 +23,7 @@ Templates.compile = function(callback) { winston.info('[minifier] Compiling templates skipped'); } - emitter.emit('templates:compiled'); - if (callback) { - callback(); - } - return; + return callback(); } var coreTemplatesPath = nconf.get('core_templates_path'), @@ -119,15 +116,20 @@ Templates.compile = function(callback) { }, function(err) { if (err) { winston.error('[meta/templates] ' + err.stack); - } else { - compileIndex(viewsPath, function() { - winston.verbose('[meta/templates] Successfully compiled templates.'); - emitter.emit('templates:compiled'); - if (callback) { - callback(); - } - }); + return callback(err); } + + compileIndex(viewsPath, function() { + winston.verbose('[meta/templates] Successfully compiled templates.'); + + emitter.emit('templates:compiled'); + if (process.send) { + process.send({ + action: 'templates:compiled' + }); + } + callback(); + }); }); }); }); diff --git a/src/meta/title.js b/src/meta/title.js index d67c448343..7522dbe3a7 100644 --- a/src/meta/title.js +++ b/src/meta/title.js @@ -40,7 +40,7 @@ module.exports = function(Meta) { }; Meta.title.parseFragment = function (urlFragment, language, locals, callback) { - var translated = ['', 'recent', 'unread', 'users', 'notifications']; + var translated = ['', 'recent', 'unread', 'users', 'notifications', 'popular', 'tags']; if (translated.indexOf(urlFragment) !== -1) { if (!urlFragment.length) { urlFragment = 'home'; @@ -60,7 +60,7 @@ module.exports = function(Meta) { } else if (tests.isTag.test(urlFragment)) { var tag = urlFragment.match(/tags\/([\s\S]+)/)[1]; - translator.translate('[[pages:tags, ' + tag + ']]', language, function(translated) { + translator.translate('[[pages:tag, ' + tag + ']]', language, function(translated) { callback(null, translated); }); } else if (tests.isUserPage.test(urlFragment)) { diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 1d6c0dae6a..5b3f4c186d 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -53,19 +53,17 @@ middleware.buildHeader = function(req, res, next) { }; middleware.renderHeader = function(req, res, next) { - var uid = req.user ? req.user.uid : 0; - var custom_header = { 'plugins': [], 'authentication': [] }; - user.getUserFields(uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) { + user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) { if (err) { return next(err); } - userData.uid = uid; + userData.uid = req.uid; userData['email:confirmed'] = parseInt(userData['email:confirmed'], 10) === 1; async.parallel({ diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index cc3ae7cb5c..8adc6aabe7 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -10,18 +10,19 @@ var app, winston = require('winston'), validator = require('validator'), nconf = require('nconf'), + ensureLoggedIn = require('connect-ensure-login'), - plugins = require('./../plugins'), - navigation = require('./../navigation'), - meta = require('./../meta'), - translator = require('./../../public/src/modules/translator'), - user = require('./../user'), - groups = require('./../groups'), - db = require('./../database'), - categories = require('./../categories'), - topics = require('./../topics'), + plugins = require('../plugins'), + navigation = require('../navigation'), + meta = require('../meta'), + translator = require('../../public/src/modules/translator'), + user = require('../user'), + groups = require('../groups'), + db = require('../database'), + categories = require('../categories'), + topics = require('../topics'), messaging = require('../messaging'), - ensureLoggedIn = require('connect-ensure-login'), + analytics = require('../analytics'), controllers = { @@ -32,6 +33,12 @@ var app, middleware.authenticate = function(req, res, next) { if (req.user) { return next(); + } else if (plugins.hasListeners('action:middleware.authenticate')) { + return plugins.fireHook('action:middleware.authenticate', { + req: req, + res: res, + next: next + }); } controllers.helpers.notAllowed(req, res); @@ -42,14 +49,19 @@ middleware.applyCSRF = csrf(); middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login'); middleware.pageView = function(req, res, next) { + analytics.pageView(req.ip); + if (req.user) { user.updateLastOnlineTime(req.user.uid); - user.updateOnlineUsers(req.user.uid); + if (req.path.startsWith('/api/users') || req.path.startsWith('/users')) { + user.updateOnlineUsers(req.user.uid, next); + } else { + user.updateOnlineUsers(req.user.uid); + next(); + } + } else { + next(); } - - analytics.pageView(req.ip); - - next(); }; middleware.redirectToAccountIfLoggedIn = function(req, res, next) { @@ -73,27 +85,6 @@ middleware.redirectToLoginIfGuest = function(req, res, next) { } }; -middleware.addSlug = function(req, res, next) { - function redirect(method, id, name) { - method(id, 'slug', function(err, slug) { - if (err || !slug || slug === id + '/') { - return next(err); - } - - controllers.helpers.redirect(res, name + encodeURI(slug)); - }); - } - - if (!req.params.slug) { - if (req.params.category_id) { - return redirect(categories.getCategoryField, req.params.category_id, '/category/'); - } else if (req.params.topic_id) { - return redirect(topics.getTopicField, req.params.topic_id, '/topic/'); - } - } - next(); -}; - middleware.validateFiles = function(req, res, next) { if (!Array.isArray(req.files.files) || !req.files.files.length) { return next(new Error(['[[error:invalid-files]]'])); @@ -124,31 +115,31 @@ middleware.checkGlobalPrivacySettings = function(req, res, next) { middleware.checkAccountPermissions = function(req, res, next) { // This middleware ensures that only the requested user and admins can pass - var callerUID = req.user ? parseInt(req.user.uid, 10) : 0; - - if (callerUID === 0) { - return controllers.helpers.notAllowed(req, res); - } - - user.getUidByUserslug(req.params.userslug, function (err, uid) { + middleware.authenticate(req, res, function(err) { if (err) { return next(err); } - if (!uid) { - return controllers.helpers.notFound(req, res); - } + user.getUidByUserslug(req.params.userslug, function (err, uid) { + if (err) { + return next(err); + } - if (parseInt(uid, 10) === callerUID) { - return next(); - } + if (!uid) { + return controllers.helpers.notFound(req, res); + } - user.isAdministrator(callerUID, function(err, isAdmin) { - if (err || isAdmin) { - return next(err); + if (parseInt(uid, 10) === req.uid) { + return next(); } - controllers.helpers.notAllowed(req, res); + user.isAdministrator(req.uid, function(err, isAdmin) { + if (err || isAdmin) { + return next(err); + } + + controllers.helpers.notAllowed(req, res); + }); }); }); }; @@ -201,144 +192,97 @@ middleware.buildHeader = function(req, res, next) { }; middleware.renderHeader = function(req, res, callback) { - var uid = req.user ? parseInt(req.user.uid, 10) : 0; - - navigation.get(function(err, menuItems) { - if (err) { - return callback(err); - } - - var defaultMetaTags = [{ - name: 'viewport', - content: 'width=device-width, initial-scale=1.0, user-scalable=no' - }, { - name: 'content-type', - content: 'text/html; charset=UTF-8' - }, { - name: 'apple-mobile-web-app-capable', - content: 'yes' - }, { - property: 'og:site_name', - content: meta.config.title || 'NodeBB' - }, { - name: 'keywords', - content: meta.config.keywords || '' - }, { - name: 'msapplication-badge', - content: 'frequency=30; polling-uri=' + nconf.get('url') + '/sitemap.xml' - }, { - name: 'msapplication-square150x150logo', - content: meta.config['brand:logo'] || '' - }], - defaultLinkTags = [{ - rel: 'apple-touch-icon', - href: nconf.get('relative_path') + '/apple-touch-icon' - }], - templateValues = { - bootswatchCSS: meta.config['theme:src'], - title: meta.config.title || '', - description: meta.config.description || '', - 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '', - 'brand:logo': meta.config['brand:logo'] || '', - 'brand:logo:display': meta.config['brand:logo']?'':'hide', - navigation: menuItems, - allowRegistration: meta.config.allowRegistration === undefined || parseInt(meta.config.allowRegistration, 10) === 1, - searchEnabled: plugins.hasListeners('filter:search.query') - }; - - for (var key in res.locals.config) { - if (res.locals.config.hasOwnProperty(key)) { - templateValues[key] = res.locals.config[key]; - } + var templateValues = { + bootswatchCSS: meta.config['theme:src'], + title: meta.config.title || '', + description: meta.config.description || '', + 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '', + 'brand:logo': meta.config['brand:logo'] || '', + 'brand:logo:display': meta.config['brand:logo']?'':'hide', + allowRegistration: meta.config.allowRegistration === undefined || parseInt(meta.config.allowRegistration, 10) === 1, + searchEnabled: plugins.hasListeners('filter:search.query') + }; + + for (var key in res.locals.config) { + if (res.locals.config.hasOwnProperty(key)) { + templateValues[key] = res.locals.config[key]; } + } - templateValues.configJSON = JSON.stringify(res.locals.config); + templateValues.configJSON = JSON.stringify(res.locals.config); - templateValues.metaTags = defaultMetaTags.concat(res.locals.metaTags || []).map(function(tag) { - if(!tag || typeof tag.content !== 'string') { - winston.warn('Invalid meta tag. ', tag); - return tag; + async.parallel({ + customCSS: function(next) { + templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1; + if (!templateValues.useCustomCSS || !meta.config.customCSS || !meta.config.renderedCustomCSS) { + return next(null, ''); } - - tag.content = validator.escape(tag.content); - return tag; - }); - - templateValues.linkTags = defaultLinkTags.concat(res.locals.linkTags || []); - templateValues.linkTags.unshift({ - rel: "icon", - type: "image/x-icon", - href: nconf.get('relative_path') + '/favicon.ico' - }); - - async.parallel({ - customCSS: function(next) { - templateValues.useCustomCSS = parseInt(meta.config.useCustomCSS, 10) === 1; - if (!templateValues.useCustomCSS || !meta.config.customCSS || !meta.config.renderedCustomCSS) { - return next(null, ''); - } - next(null, meta.config.renderedCustomCSS); - }, - customJS: function(next) { - templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1; - next(null, templateValues.useCustomJS ? meta.config.customJS : ''); - }, - title: function(next) { - if (uid) { - user.getSettings(uid, function(err, settings) { - if (err) { - return next(err); - } - meta.title.build(req.url.slice(1), settings.userLang, res.locals, next); - }); - } else { - meta.title.build(req.url.slice(1), meta.config.defaultLang, res.locals, next); - } - }, - isAdmin: function(next) { - user.isAdministrator(uid, next); - }, - user: function(next) { - if (uid) { - user.getUserFields(uid, ['username', 'userslug', 'email', 'picture', 'status', 'email:confirmed', 'banned'], next); - } else { - next(null, { - username: '[[global:guest]]', - userslug: '', - picture: user.createGravatarURLFromEmail(''), - status: 'offline', - banned: false, - uid: 0 - }); - } + next(null, meta.config.renderedCustomCSS); + }, + customJS: function(next) { + templateValues.useCustomJS = parseInt(meta.config.useCustomJS, 10) === 1; + next(null, templateValues.useCustomJS ? meta.config.customJS : ''); + }, + title: function(next) { + if (req.uid) { + user.getSettings(req.uid, function(err, settings) { + if (err) { + return next(err); + } + meta.title.build(req.url.slice(1), settings.userLang, res.locals, next); + }); + } else { + meta.title.build(req.url.slice(1), meta.config.defaultLang, res.locals, next); } - }, function(err, results) { - if (err) { - return callback(err); + }, + isAdmin: function(next) { + user.isAdministrator(req.uid, next); + }, + user: function(next) { + if (req.uid) { + user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'status', 'email:confirmed', 'banned'], next); + } else { + next(null, { + username: '[[global:guest]]', + userslug: '', + picture: user.createGravatarURLFromEmail(''), + status: 'offline', + banned: false, + uid: 0 + }); } + }, + navigation: async.apply(navigation.get), + tags: async.apply(meta.tags.parse, res.locals.metaTags, res.locals.linkTags) + }, function(err, results) { + if (err) { + return callback(err); + } - if (results.user && parseInt(results.user.banned, 10) === 1) { - req.logout(); - res.redirect('/'); - return; - } - results.user.isAdmin = results.isAdmin || false; - results.user.uid = parseInt(results.user.uid, 10); - results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1; - - templateValues.browserTitle = results.title; - templateValues.isAdmin = results.user.isAdmin; - templateValues.user = results.user; - templateValues.userJSON = JSON.stringify(results.user); - templateValues.customCSS = results.customCSS; - templateValues.customJS = results.customJS; - templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin; - - templateValues.template = {name: res.locals.template}; - templateValues.template[res.locals.template] = true; - - app.render('header', templateValues, callback); - }); + if (results.user && parseInt(results.user.banned, 10) === 1) { + req.logout(); + res.redirect('/'); + return; + } + results.user.isAdmin = results.isAdmin || false; + results.user.uid = parseInt(results.user.uid, 10); + results.user['email:confirmed'] = parseInt(results.user['email:confirmed'], 10) === 1; + + templateValues.browserTitle = results.title; + templateValues.navigation = results.navigation + templateValues.metaTags = results.tags.meta; + templateValues.linkTags = results.tags.link; + templateValues.isAdmin = results.user.isAdmin; + templateValues.user = results.user; + templateValues.userJSON = JSON.stringify(results.user); + templateValues.customCSS = results.customCSS; + templateValues.customJS = results.customJS; + templateValues.maintenanceHeader = parseInt(meta.config.maintenanceMode, 10) === 1 && !results.isAdmin; + + templateValues.template = {name: res.locals.template}; + templateValues.template[res.locals.template] = true; + + app.render('header', templateValues, callback); }); }; @@ -499,11 +443,11 @@ middleware.maintenanceMode = function(req, res, next) { }); }; -middleware.publicTagListing = function(req, res, next) { - if (req.user || (!meta.config.hasOwnProperty('publicTagListing') || parseInt(meta.config.publicTagListing, 10) === 1)) { - next(); - } else { +middleware.privateTagListing = function(req, res, next) { + if (!req.user && parseInt(meta.config.privateTagListing, 10) === 1) { controllers.helpers.notAllowed(req, res); + } else { + next(); } }; @@ -527,7 +471,7 @@ middleware.exposeUid = function(req, res, next) { res.locals.uid = uid; next(); - }) + }); } else { next(); } diff --git a/src/navigation/index.js b/src/navigation/index.js index b9a8a99e16..6c8be11d2f 100644 --- a/src/navigation/index.js +++ b/src/navigation/index.js @@ -10,19 +10,22 @@ var navigation = {}, navigation.get = function(callback) { admin.get(function(err, data) { - callback(err, data - .filter(function(item) { - return item.enabled; - }) - .map(function(item) { - for (var i in item) { - if (item.hasOwnProperty(i)) { - item[i] = translator.unescape(item[i]); - } + if (err) { + return callback(err); + } + + data = data.filter(function(item) { + return item && item.enabled; + }).map(function(item) { + for (var i in item) { + if (item.hasOwnProperty(i)) { + item[i] = translator.unescape(item[i]); } + } + return item; + }); - return item; - })); + callback(null, data); }); }; diff --git a/src/pagination.js b/src/pagination.js index d3a3ca9dd9..58813c391b 100644 --- a/src/pagination.js +++ b/src/pagination.js @@ -13,11 +13,8 @@ pagination.create = function(currentPage, pageCount, queryObj) { pages: [] }; } - - var pagesToShow = [1]; - if (pageCount !== 1) { - pagesToShow.push(pageCount); - } + pageCount = parseInt(pageCount, 10); + var pagesToShow = [1, 2, pageCount - 1, pageCount]; currentPage = parseInt(currentPage, 10) || 1; var previous = Math.max(1, currentPage - 1); @@ -25,13 +22,12 @@ pagination.create = function(currentPage, pageCount, queryObj) { var startPage = currentPage - 2; for(var i=0; i<5; ++i) { - var p = startPage + i; - if (p >= 1 && p <= pageCount && pagesToShow.indexOf(p) === -1) { - pagesToShow.push(startPage + i); - } + pagesToShow.push(startPage + i); } - pagesToShow.sort(function(a, b) { + pagesToShow = pagesToShow.filter(function(page, index, array) { + return page > 0 && page <= pageCount && array.indexOf(page) === index; + }).sort(function(a, b) { return a - b; }); @@ -42,12 +38,17 @@ pagination.create = function(currentPage, pageCount, queryObj) { return {page: page, active: page === currentPage, qs: qs.stringify(queryObj)}; }); - var data = { - prev: {page: previous, active: currentPage > 1}, - next: {page: next, active: currentPage < pageCount}, - rel: [], - pages: pages - }; + for (i=pages.length - 1; i>0; --i) { + if (pages[i - 1].page !== pages[i].page - 1) { + pages.splice(i, 0, {separator: true}); + } + } + + var data = {rel: [], pages: pages}; + queryObj.page = previous; + data.prev = {page: previous, active: currentPage > 1, qs: qs.stringify(queryObj)}; + queryObj.page = next; + data.next = {page: next, active: currentPage < pageCount, qs: qs.stringify(queryObj)}; if (currentPage < pageCount) { data.rel.push({ diff --git a/src/plugins.js b/src/plugins.js index 3a9d1ff5fe..156ba49a3b 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -39,9 +39,10 @@ var fs = require('fs'), Plugins.libraryPaths.push(libraryPath); }; - Plugins.init = function(nbbApp, nbbMiddleware) { + Plugins.init = function(nbbApp, nbbMiddleware, callback) { + callback = callback || function() {}; if (Plugins.initialized) { - return; + return callback(); } app = nbbApp; @@ -55,7 +56,7 @@ var fs = require('fs'), Plugins.reload(function(err) { if (err) { winston.error('[plugins] NodeBB encountered a problem while loading plugins', err.message); - return; + return callback(err); } if (global.env === 'development') { @@ -64,6 +65,7 @@ var fs = require('fs'), Plugins.initialized = true; emitter.emit('plugins:loaded'); + callback(); }); Plugins.registerHook('core', { @@ -72,14 +74,6 @@ var fs = require('fs'), }); }; - Plugins.ready = function(callback) { - if (!Plugins.initialized) { - emitter.once('plugins:loaded', callback); - } else { - callback(); - } - }; - Plugins.reload = function(callback) { // Resetting all local plugin data Plugins.libraries = {}; @@ -120,19 +114,23 @@ var fs = require('fs'), }); next(); - }, - async.apply(Plugins.reloadRoutes) + } ], callback); }; Plugins.reloadRoutes = function(callback) { + callback = callback || function() {}; var router = express.Router(); router.hotswapId = 'plugins'; router.render = function() { app.render.apply(app, arguments); }; - Plugins.fireHook('static:app.load', {app: app, router: router, middleware: middleware, controllers: controllers}, function() { + Plugins.fireHook('static:app.load', {app: app, router: router, middleware: middleware, controllers: controllers}, function(err) { + if (err) { + return winston.error('[plugins] Encountered error while executing post-router plugins hooks: ' + err.message); + } + hotswap.replace('plugins', router); winston.verbose('[plugins] All plugins reloaded and rerouted'); callback(); @@ -245,15 +243,6 @@ var fs = require('fs'), }); }; - function getLatestVersion(versions) { - for(var version in versions) { - if (versions.hasOwnProperty(version) && versions[version] === 'latest') { - return version; - } - } - return ''; - } - Plugins.showInstalled = function(callback) { var npmPluginPath = path.join(__dirname, '../node_modules'); @@ -339,7 +328,7 @@ var fs = require('fs'), winston.verbose('[plugins] Plugin libraries removed from Node.js cache'); next(); - }, + } ], next); }; diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 00f6cc9f00..491fa0d747 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -55,12 +55,8 @@ module.exports = function(Plugins) { callback = typeof callback === 'function' ? callback : function() {}; var hookList = Plugins.loadedHooks[hook]; - - if (!Array.isArray(hookList) || !hookList.length) { - return callback(null, params); - } - var hookType = hook.split(':')[0]; + switch (hookType) { case 'filter': fireFilterHook(hook, hookList, params, callback); @@ -78,6 +74,10 @@ module.exports = function(Plugins) { }; function fireFilterHook(hook, hookList, params, callback) { + if (!Array.isArray(hookList) || !hookList.length) { + return callback(null, params); + } + async.reduce(hookList, params, function(params, hookObj, next) { if (typeof hookObj.method !== 'function') { if (global.env === 'development') { @@ -98,6 +98,9 @@ module.exports = function(Plugins) { } function fireActionHook(hook, hookList, params, callback) { + if (!Array.isArray(hookList) || !hookList.length) { + return callback(); + } async.each(hookList, function(hookObj, next) { if (typeof hookObj.method !== 'function') { @@ -113,6 +116,9 @@ module.exports = function(Plugins) { } function fireStaticHook(hook, hookList, params, callback) { + if (!Array.isArray(hookList) || !hookList.length) { + return callback(); + } async.each(hookList, function(hookObj, next) { if (typeof hookObj.method === 'function') { var timedOut = false; diff --git a/src/plugins/load.js b/src/plugins/load.js index 0637427a12..619e2a2131 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -5,7 +5,7 @@ var fs = require('fs'), semver = require('semver'), async = require('async'), winston = require('winston'), - pkg = require('../../package.json'), + nconf = require('nconf'), utils = require('../../public/src/utils'); @@ -62,7 +62,7 @@ module.exports = function(Plugins) { } if (pluginData.nbbpm && pluginData.nbbpm.compatibility && semver.validRange(pluginData.nbbpm.compatibility)) { - if (!semver.gtr(pkg.version, pluginData.nbbpm.compatibility)) { + if (!semver.gtr(nconf.get('version'), pluginData.nbbpm.compatibility)) { display(); } } else { diff --git a/src/postTools.js b/src/postTools.js index cb3b1da938..d702274abf 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -1,138 +1,15 @@ 'use strict'; -var winston = require('winston'), - async = require('async'), - nconf = require('nconf'), - validator = require('validator'), +var async = require('async'), - db = require('./database'), posts = require('./posts'), - topics = require('./topics'), - threadTools = require('./threadTools'), privileges = require('./privileges'), - user = require('./user'), - utils = require('../public/src/utils'), - plugins = require('./plugins'), - events = require('./events'), - meta = require('./meta'), - LRU = require('lru-cache'); - -var cache = LRU({ - max: 1048576, - length: function (n) { return n.length; }, - maxAge: 1000 * 60 * 60 -}); + cache = require('./posts/cache'); (function(PostTools) { PostTools.edit = function(data, callback) { - var options = data.options || {}, - title = data.title.trim(); - - async.waterfall([ - function (next) { - privileges.posts.canEdit(data.pid, data.uid, next); - }, - function(canEdit, next) { - if (!canEdit) { - return next(new Error('[[error:no-privileges]]')); - } - posts.getPostData(data.pid, next); - }, - function(postData, next) { - postData.content = data.content; - plugins.fireHook('filter:post.edit', {post: postData, uid: data.uid}, next); - } - ], function(err, result) { - if (err) { - return callback(err); - } - - var postData = result.post; - async.parallel({ - post: function(next) { - var d = { - edited: Date.now(), - editor: data.uid, - content: postData.content - }; - if (data.handle) { - d.handle = data.handle; - } - posts.setPostFields(data.pid, d, next); - }, - topic: function(next) { - var tid = postData.tid; - async.parallel({ - cid: function(next) { - topics.getTopicField(tid, 'cid', next); - }, - isMain: function(next) { - posts.isMain(data.pid, next); - } - }, function(err, results) { - if (err) { - return next(err); - } - - options.tags = options.tags || []; - - if (!results.isMain) { - return next(null, { - tid: tid, - cid: results.cid, - isMainPost: false - }); - } - - var topicData = { - tid: tid, - cid: results.cid, - uid: postData.uid, - mainPid: data.pid, - title: title, - slug: tid + '/' + utils.slugify(title) - }; - if (options.topic_thumb) { - topicData.thumb = options.topic_thumb; - } - - db.setObject('topic:' + tid, topicData, function(err) { - plugins.fireHook('action:topic.edit', topicData); - }); - - topics.updateTags(tid, options.tags, function(err) { - if (err) { - return next(err); - } - topics.getTopicTagsObjects(tid, function(err, tags) { - next(err, { - tid: tid, - cid: results.cid, - uid: postData.uid, - title: validator.escape(title), - isMainPost: results.isMain, - tags: tags - }); - }); - }); - }); - }, - postData: function(next) { - cache.del(postData.pid); - PostTools.parsePost(postData, next); - } - }, function(err, results) { - if (err) { - return callback(err); - } - postData.cid = results.topic.cid; - results.content = results.postData.content; - - plugins.fireHook('action:post.edit', postData); - callback(null, results); - }); - }); + posts.edit(data, callback); }; PostTools.delete = function(uid, pid, callback) { @@ -176,7 +53,7 @@ var cache = LRU({ if (err) { return callback(err); } - PostTools.parsePost(postData, callback); + posts.parsePost(postData, callback); }); } }); @@ -192,32 +69,5 @@ var cache = LRU({ }); }; - PostTools.parsePost = function(postData, callback) { - postData.content = postData.content || ''; - - var cachedContent = cache.get(postData.pid); - if (cachedContent) { - postData.content = cachedContent; - return callback(null, postData); - } - - plugins.fireHook('filter:parse.post', {postData: postData}, function(err, data) { - if (err) { - return callback(err); - } - cache.set(data.postData.pid, data.postData.content); - callback(null, data.postData); - }); - }; - - PostTools.parseSignature = function(userData, uid, callback) { - userData.signature = userData.signature || ''; - - plugins.fireHook('filter:parse.signature', {userData: userData, uid: uid}, callback); - }; - - PostTools.resetCache = function() { - cache.reset(); - }; }(exports)); diff --git a/src/posts.js b/src/posts.js index 53931d75f7..c2e252302d 100644 --- a/src/posts.js +++ b/src/posts.js @@ -7,7 +7,6 @@ var async = require('async'), utils = require('../public/src/utils'), user = require('./user'), topics = require('./topics'), - postTools = require('./postTools'), privileges = require('./privileges'), plugins = require('./plugins'); @@ -15,6 +14,8 @@ var async = require('async'), require('./posts/create')(Posts); require('./posts/delete')(Posts); + require('./posts/edit')(Posts); + require('./posts/parse')(Posts); require('./posts/user')(Posts); require('./posts/topics')(Posts); require('./posts/category')(Posts); @@ -26,57 +27,52 @@ var async = require('async'), db.isSortedSetMember('posts:pid', pid, callback); }; - Posts.getPidsFromSet = function(set, start, end, reverse, callback) { - if (isNaN(start) || isNaN(end)) { + Posts.getPidsFromSet = function(set, start, stop, reverse, callback) { + if (isNaN(start) || isNaN(stop)) { return callback(null, []); } - db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, end, callback); + db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange'](set, start, stop, callback); }; Posts.getPostsByPids = function(pids, uid, callback) { var keys = []; - for(var x=0, numPids=pids.length; x 0) { feed.pubDate = new Date(parseInt(feedTopics[0].lastposttime, 10)).toUTCString(); } - feedTopics.forEach(function(topicData) { - if (topicData && topicData.teaser && topicData.teaser.user) { - feed.item({ - title: topicData.title, - description: topicData.teaser.content, - url: nconf.get('url') + '/topic/' + topicData.slug, - author: topicData.teaser.user.username, - date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() - }); + async.map(feedTopics, function(topicData, next) { + var feedItem = { + title: topicData.title, + url: nconf.get('url') + '/topic/' + topicData.slug, + date: new Date(parseInt(topicData.lastposttime, 10)).toUTCString() + }; + + if (topicData.teaser && topicData.teaser.user) { + feedItem.description = topicData.teaser.content; + feedItem.author = topicData.teaser.user.username; + return next(null, feedItem); } - }); - callback(null, feed); + topics.getMainPost(topicData.tid, feedOptions.uid, function(err, mainPost) { + if (err) { + return next(err); + } + if (!mainPost) { + return next(null, feedItem); + } + feedItem.description = mainPost.content; + feedItem.author = mainPost.user.username; + next(null, feedItem); + }); + }, function(err, feedItems) { + if (err) { + return callback(err); + } + feedItems.forEach(function(feedItem) { + if (feedItem) { + feed.item(feedItem); + } + }); + callback(null, feed); + }); } function generateForRecentPosts(req, res, next) { - var uid = req.user ? req.user.uid : 0; - posts.getRecentPosts(uid, 0, 19, 'month', function(err, posts) { + posts.getRecentPosts(req.uid, 0, 19, 'month', function(err, posts) { if (err) { return next(err); } @@ -257,7 +279,6 @@ function generateForRecentPosts(req, res, next) { } function generateForCategoryRecentPosts(req, res, next) { - var uid = req.user ? req.user.uid : 0; var cid = req.params.category_id; async.parallel({ @@ -265,7 +286,7 @@ function generateForCategoryRecentPosts(req, res, next) { categories.getCategoryData(cid, next); }, posts: function(next) { - categories.getRecentReplies(cid, uid, 20, next); + categories.getRecentReplies(cid, req.uid, 20, next); } }, function(err, results) { if (err) { diff --git a/src/routes/index.js b/src/routes/index.js index 5efca3f067..2ebf77d53e 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -42,12 +42,12 @@ function topicRoutes(app, middleware, controllers) { app.get('/api/topic/teaser/:topic_id', controllers.topics.teaser); setupPageRoute(app, '/topic/:topic_id/:slug/:post_index?', middleware, [], controllers.topics.get); - setupPageRoute(app, '/topic/:topic_id/:slug?', middleware, [middleware.addSlug], controllers.topics.get); + setupPageRoute(app, '/topic/:topic_id/:slug?', middleware, [], controllers.topics.get); } function tagRoutes(app, middleware, controllers) { - setupPageRoute(app, '/tags/:tag', middleware, [middleware.publicTagListing], controllers.tags.getTag); - setupPageRoute(app, '/tags', middleware, [middleware.publicTagListing], controllers.tags.getTags); + setupPageRoute(app, '/tags/:tag', middleware, [middleware.privateTagListing], controllers.tags.getTag); + setupPageRoute(app, '/tags', middleware, [middleware.privateTagListing], controllers.tags.getTags); } function categoryRoutes(app, middleware, controllers) { @@ -58,7 +58,7 @@ function categoryRoutes(app, middleware, controllers) { app.get('/api/unread/total', middleware.authenticate, controllers.categories.unreadTotal); setupPageRoute(app, '/category/:category_id/:slug/:topic_index', middleware, [], controllers.categories.get); - setupPageRoute(app, '/category/:category_id/:slug?', middleware, [middleware.addSlug], controllers.categories.get); + setupPageRoute(app, '/category/:category_id/:slug?', middleware, [], controllers.categories.get); } function accountRoutes(app, middleware, controllers) { @@ -118,7 +118,9 @@ module.exports = function(app, middleware) { app.all(relativePath + '/api/?*', middleware.prepareAPI); app.all(relativePath + '/api/admin/?*', middleware.isAdmin); - app.all(relativePath + '/admin/?*', middleware.ensureLoggedIn, middleware.applyCSRF, middleware.isAdmin); + + var ensureLoggedIn = require('connect-ensure-login'); + app.all(relativePath + '/admin/?*', ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login?local=1'), middleware.applyCSRF, middleware.isAdmin); adminRoutes(router, middleware, controllers); metaRoutes(router, middleware, controllers); @@ -152,7 +154,7 @@ module.exports = function(app, middleware) { if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) { return next(); } - if (req.path.indexOf('/uploads/files') === 0) { + if (req.path.startsWith('/uploads/files')) { return res.status(403).json('not-allowed'); } next(); @@ -167,7 +169,7 @@ module.exports = function(app, middleware) { // Add plugin routes - plugins.init(app, middleware); + plugins.reloadRoutes(); authRoutes.reloadRoutes(); }; diff --git a/src/search.js b/src/search.js index 063ebfa142..09e22a7a44 100644 --- a/src/search.js +++ b/src/search.js @@ -27,6 +27,7 @@ search.search = function(data, callback) { } result[searchIn] = data.matches; result.matchCount = data.matchCount; + result.pageCount = data.pageCount; result.time = (process.elapsedTimeSince(start) / 1000).toFixed(2); callback(null, result); } @@ -45,7 +46,7 @@ search.search = function(data, callback) { if (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts') { searchInContent(data, done); } else if (searchIn === 'users') { - searchInUsers(query, data.uid, done); + searchInUsers(data, done); } else if (searchIn === 'tags') { searchInTags(query, done); } else { @@ -89,7 +90,7 @@ function searchInContent(data, callback) { var matchCount = 0; if (!results || (!results.pids.length && !results.tids.length)) { - return callback(null, {matches: [], matchCount: matchCount}); + return callback(null, {matches: [], matchCount: matchCount, pageCount: 1}); } async.waterfall([ @@ -113,10 +114,10 @@ function searchInContent(data, callback) { pids = pids.slice(start, start + 10); } - posts.getPostSummaryByPids(pids, data.uid, {stripTags: true, parse: false}, next); + posts.getPostSummaryByPids(pids, data.uid, {}, next); }, function(posts, next) { - next(null, {matches: posts, matchCount: matchCount}); + next(null, {matches: posts, matchCount: matchCount, pageCount: Math.max(1, Math.ceil(parseInt(matchCount, 10) / 10))}); } ], callback); }); @@ -148,8 +149,8 @@ function filterAndSort(pids, data, callback) { } function getMatchedPosts(pids, data, callback) { - var postFields = ['pid', 'tid', 'timestamp']; - var topicFields = []; + var postFields = ['pid', 'tid', 'timestamp', 'deleted']; + var topicFields = ['deleted']; var categoryFields = []; if (data.replies) { @@ -179,7 +180,9 @@ function getMatchedPosts(pids, data, callback) { db.getObjectsFields(keys, postFields, next); }, function(_posts, next) { - posts = _posts; + posts = _posts.filter(function(post) { + return post && parseInt(post.deleted, 10) !== 1; + }); async.parallel({ users: function(next) { @@ -193,10 +196,6 @@ function getMatchedPosts(pids, data, callback) { } }, topics: function(next) { - if (!topicFields.length) { - return next(); - } - var topics; async.waterfall([ function(next) { @@ -267,6 +266,10 @@ function getMatchedPosts(pids, data, callback) { } }); + posts = posts.filter(function(post) { + return post && post.topic && parseInt(post.topic.deleted, 10) !== 1; + }); + next(null, posts); } ], callback); @@ -363,10 +366,22 @@ function sortPosts(posts, data) { } function getSearchCids(data, callback) { - if (!Array.isArray(data.categories) || !data.categories.length || data.categories.indexOf('all') !== -1) { + if (!Array.isArray(data.categories) || !data.categories.length) { return callback(null, []); } + if (data.categories.indexOf('all') !== -1) { + async.waterfall([ + function(next) { + db.getSortedSetRange('categories:cid', 0, -1, next); + }, + function(cids, next) { + privileges.categories.filterCids('read', cids, data.uid, next); + } + ], callback); + return; + } + async.parallel({ watchedCids: function(next) { if (data.categories.indexOf('watched') !== -1) { @@ -420,12 +435,12 @@ function getSearchUids(data, callback) { } } -function searchInUsers(query, uid, callback) { - user.search({query: query, uid: uid}, function(err, results) { +function searchInUsers(data, callback) { + user.search(data, function(err, results) { if (err) { return callback(err); } - callback(null, {matches: results.users, matchCount: results.matchCount}); + callback(null, {matches: results.users, matchCount: results.matchCount, pageCount: results.pageCount}); }); } @@ -435,7 +450,7 @@ function searchInTags(query, callback) { return callback(err); } - callback(null, {matches: tags, matchCount: tags.length}); + callback(null, {matches: tags, matchCount: tags.length, pageCount: 1}); }); } diff --git a/src/sitemap.js b/src/sitemap.js index aab7451a61..0c6d016e36 100644 --- a/src/sitemap.js +++ b/src/sitemap.js @@ -87,7 +87,7 @@ sitemap.getDynamicUrls = function(callback) { db.getSortedSetRevRange('topics:recent', 0, parseInt(meta.config.sitemapTopics, 10) || -1, next); }, function(tids, next) { - privileges.topics.filter('read', tids, 0, next); + privileges.topics.filterTids('read', tids, 0, next); }, function(tids, next) { topics.getTopicsFields(tids, ['tid', 'title', 'lastposttime'], next); diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 6b61d523f3..3ca18e6e7f 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -104,12 +104,12 @@ SocketAdmin.themes.updateBranding = function(socket, data, callback) { }; SocketAdmin.plugins.toggleActive = function(socket, plugin_id, callback) { - require('../postTools').resetCache(); + require('../posts/cache').reset(); plugins.toggleActive(plugin_id, callback); }; SocketAdmin.plugins.toggleInstall = function(socket, data, callback) { - require('../postTools').resetCache(); + require('../posts/cache').reset(); plugins.toggleInstall(data.id, data.version, callback); }; @@ -234,7 +234,10 @@ SocketAdmin.analytics.get = function(socket, data, callback) { monthlyPageViews: function(next) { getMonthlyPageViews(next); } - }, callback); + }, function(err, data) { + data.pastDay = data.pageviews.reduce(function(a, b) {return parseInt(a, 10) + parseInt(b, 10);}); + callback(err, data); + }); } } else { callback(new Error('Invalid analytics call')); @@ -304,12 +307,12 @@ SocketAdmin.getMoreEvents = function(socket, next, callback) { if (start < 0) { return callback(null, {data: [], next: next}); } - var end = start + 10; - events.getEvents(start, end, function(err, events) { + var stop = start + 10; + events.getEvents(start, stop, function(err, events) { if (err) { return callback(err); } - callback(null, {events: events, next: end + 1}); + callback(null, {events: events, next: stop + 1}); }); }; @@ -336,15 +339,15 @@ SocketAdmin.getMoreFlags = function(socket, data, callback) { var sortBy = data.sortBy || 'count'; var byUsername = data.byUsername || ''; var start = parseInt(data.after, 10); - var end = start + 19; + var stop = start + 19; if (byUsername) { - posts.getUserFlags(byUsername, sortBy, socket.uid, start, end, function(err, posts) { - callback(err, {posts: posts, next: end + 1}); + posts.getUserFlags(byUsername, sortBy, socket.uid, start, stop, function(err, posts) { + callback(err, {posts: posts, next: stop + 1}); }); } else { var set = sortBy === 'count' ? 'posts:flags:count' : 'posts:flagged'; - posts.getFlags(set, socket.uid, start, end, function(err, posts) { - callback(err, {posts: posts, next: end + 1}); + posts.getFlags(set, socket.uid, start, stop, function(err, posts) { + callback(err, {posts: posts, next: stop + 1}); }); } }; diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index 0fa583d742..454a51741a 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -1,7 +1,7 @@ "use strict"; -var async = require('async'), +var async = require('async'), db = require('../../database'), groups = require('../../groups'), user = require('../../user'), @@ -132,7 +132,7 @@ User.sendValidationEmail = function(socket, uids, callback) { if (!Array.isArray(uids)) { return callback(new Error('[[error:invalid-data]]')); } - + if (parseInt(meta.config.requireEmailConfirmation, 10) !== 1) { return callback(new Error('[[error:email-confirmations-are-disabled]]')); } @@ -144,7 +144,7 @@ User.sendValidationEmail = function(socket, uids, callback) { async.eachLimit(usersData, 50, function(userData, next) { if (userData.email && userData.uid) { - user.email.verify(userData.uid, userData.email, next); + user.email.sendValidationEmail(userData.uid, userData.email, next); } else { next(); } diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index d98cb0c21f..8ed8881f68 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -58,7 +58,7 @@ SocketCategories.loadMore = function(socket, data, callback) { } var start = parseInt(data.after, 10), - end = start + results.settings.topicsPerPage - 1; + stop = start + results.settings.topicsPerPage - 1; if (results.targetUid) { set = 'cid:' + data.cid + ':uid:' + results.targetUid + ':tids'; @@ -69,7 +69,7 @@ SocketCategories.loadMore = function(socket, data, callback) { set: set, reverse: reverse, start: start, - stop: end, + stop: stop, uid: socket.uid, targetUid: results.targetUid }, function(err, data) { diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js index 60bb51bd4e..12ac8279c8 100644 --- a/src/socket.io/meta.js +++ b/src/socket.io/meta.js @@ -89,7 +89,6 @@ SocketMeta.rooms.getAll = function(socket, data, callback) { users: { categories: roomClients.categories ? roomClients.categories.length : 0, recent: roomClients.recent_posts ? roomClients.recent_posts.length : 0, - tags: roomClients.tags ? roomClients.tags.length : 0, topics: 0, category: 0 }, diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 2825b28656..f160c9765a 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -139,69 +139,30 @@ SocketModules.chats.send = function(socket, data, callback) { return callback(new Error('[[error:invalid-data]]')); } - if (parseInt(meta.config.disableChat) === 1) { - return callback(new Error('[[error:chat-disabled]]')); - } - - var touid = parseInt(data.touid, 10); - if (touid === socket.uid || socket.uid === 0) { - return; - } - - var msg = S(data.message).stripTags().s; + var now = Date.now(), + touid = parseInt(data.touid, 10); - var now = Date.now(); + // Websocket rate limiting socket.lastChatMessageTime = socket.lastChatMessageTime || 0; - if (now - socket.lastChatMessageTime < 200) { return callback(new Error('[[error:too-many-messages]]')); + } else { + socket.lastChatMessageTime = now; } - socket.lastChatMessageTime = now; - - user.getUserFields(socket.uid, ['banned', 'email:confirmed'], function(err, userData) { - if (err) { - return callback(err); - } - - if (parseInt(userData.banned, 10) === 1) { - return callback(new Error('[[error:user-banned]]')); + Messaging.canMessage(socket.uid, touid, function(err, allowed) { + if (err || !allowed) { + return callback(err || new Error('[[error:chat-restricted]]')); } - if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && parseInt(userData['email:confirmed'], 10) !== 1) { - return callback(new Error('[[error:email-not-confirmed-chat]]')); - } - - Messaging.canMessage(socket.uid, touid, function(err, allowed) { - if (err || !allowed) { - return callback(err || new Error('[[error:chat-restricted]]')); + Messaging.addMessage(socket.uid, touid, data.message, function(err, message) { + if (err) { + return callback(err); } - Messaging.addMessage(socket.uid, touid, msg, function(err, message) { - if (err) { - return callback(err); - } - - Messaging.notifyUser(socket.uid, touid, message); - - // Recipient - SocketModules.chats.pushUnreadCount(touid); - server.in('uid_' + touid).emit('event:chats.receive', { - withUid: socket.uid, - message: message, - self: 0 - }); - - // Sender - SocketModules.chats.pushUnreadCount(socket.uid); - server.in('uid_' + socket.uid).emit('event:chats.receive', { - withUid: touid, - message: message, - self: 1 - }); + Messaging.notifyUser(socket.uid, touid, message); - callback(); - }); + callback(); }); }); }; @@ -212,19 +173,10 @@ SocketModules.chats.canMessage = function(socket, toUid, callback) { }); }; -SocketModules.chats.pushUnreadCount = function(uid) { - Messaging.getUnreadCount(uid, function(err, unreadCount) { - if (err) { - return; - } - server.in('uid_' + uid).emit('event:unread.updateChatCount', null, unreadCount); - }); -}; - SocketModules.chats.markRead = function(socket, touid, callback) { Messaging.markRead(socket.uid, touid, function(err) { if (!err) { - SocketModules.chats.pushUnreadCount(socket.uid); + Messaging.pushUnreadCount(socket.uid); } }); }; @@ -249,9 +201,9 @@ SocketModules.chats.getRecentChats = function(socket, data, callback) { return callback(new Error('[[error:invalid-data]]')); } var start = parseInt(data.after, 10), - end = start + 9; + stop = start + 9; - Messaging.getRecentChats(socket.uid, start, end, callback); + Messaging.getRecentChats(socket.uid, start, stop, callback); }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 2fdf4425b5..14b294da39 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -234,7 +234,7 @@ SocketPosts.sendNotificationToPostOwner = function(pid, fromuid, notification) { async.parallel({ username: async.apply(user.getUserField, fromuid, 'username'), topicTitle: async.apply(topics.getTopicField, postData.tid, 'title'), - postObj: async.apply(postTools.parsePost, postData) + postObj: async.apply(posts.parsePost, postData) }, function(err, results) { if (err) { return; @@ -276,13 +276,13 @@ SocketPosts.getRawPost = function(socket, pid, callback) { }; SocketPosts.edit = function(socket, data, callback) { - if(!socket.uid) { + if (!socket.uid) { return callback(new Error('[[error:not-logged-in]]')); - } else if(!data || !data.pid || !data.title || !data.content) { + } else if (!data || !data.pid || !data.content) { return callback(new Error('[[error:invalid-data]]')); - } else if (!data.title || data.title.length < parseInt(meta.config.minimumTitleLength, 10)) { + } else if (data.title && data.title.length < parseInt(meta.config.minimumTitleLength, 10)) { return callback(new Error('[[error:title-too-short, ' + meta.config.minimumTitleLength + ']]')); - } else if (data.title.length > parseInt(meta.config.maximumTitleLength, 10)) { + } else if (data.title && data.title.length > parseInt(meta.config.maximumTitleLength, 10)) { return callback(new Error('[[error:title-too-long, ' + meta.config.maximumTitleLength + ']]')); } else if (!data.content || data.content.length < parseInt(meta.config.minimumPostLength, 10)) { return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); @@ -290,32 +290,44 @@ SocketPosts.edit = function(socket, data, callback) { return callback(new Error('[[error:content-too-long, ' + meta.config.maximumPostLength + ']]')); } - // uid, pid, title, content, options postTools.edit({ uid: socket.uid, handle: data.handle, pid: data.pid, title: data.title, content: data.content, - options: { - topic_thumb: data.topic_thumb, - tags: data.tags - } - }, function(err, results) { + topic_thumb: data.topic_thumb, + tags: data.tags + }, function(err, result) { if (err) { return callback(err); } - websockets.in('topic_' + results.topic.tid).emit('event:post_edited', { - pid: data.pid, - handle: data.handle, - title: results.topic.title, - isMainPost: results.topic.isMainPost, - tags: results.topic.tags, - content: results.content - }); + if (parseInt(result.post.deleted) !== 1) { + websockets.in('topic_' + result.topic.tid).emit('event:post_edited', result); + return callback(); + } + socket.emit('event:post_edited', result); callback(); + + async.parallel({ + admins: async.apply(groups.getMembers, 'administrators', 0, -1), + moderators: async.apply(groups.getMembers, 'cid:' + result.topic.cid + ':privileges:mods', 0, -1), + uidsInTopic: async.apply(websockets.getUidsInRoom, 'topic_' + result.topic.tid) + }, function(err, results) { + if (err) { + return winston.error(err); + } + + var uids = results.uidsInTopic.filter(function(uid) { + return results.admins.indexOf(uid) !== -1 || results.moderators.indexOf(uid) !== -1; + }); + + uids.forEach(function(uid) { + websockets.in('uid_' + uid).emit('event:post_edited', result); + }); + }); }); }; @@ -481,7 +493,7 @@ SocketPosts.flag = function(socket, pid, callback) { function(topic, next) { post.topic = topic; message = '[[notifications:user_flagged_post_in, ' + userName + ', ' + topic.title + ']]'; - postTools.parsePost(post, next); + posts.parsePost(post, next); }, function(post, next) { async.parallel({ @@ -511,27 +523,23 @@ SocketPosts.flag = function(socket, pid, callback) { }; SocketPosts.loadMoreFavourites = function(socket, data, callback) { - if(!data || !data.after) { - return callback(new Error('[[error:invalid-data]]')); - } - - var start = parseInt(data.after, 10), - end = start + 9; - - posts.getPostsFromSet('uid:' + socket.uid + ':favourites', socket.uid, start, end, callback); + loadMorePosts('uid:' + data.uid + ':favourites', socket.uid, data, callback); }; SocketPosts.loadMoreUserPosts = function(socket, data, callback) { - if(!data || !data.uid || !utils.isNumber(data.after)) { + loadMorePosts('uid:' + data.uid + ':posts', socket.uid, data, callback); +}; + +function loadMorePosts(set, uid, data, callback) { + if (!data || !utils.isNumber(data.uid) || !utils.isNumber(data.after)) { return callback(new Error('[[error:invalid-data]]')); } var start = Math.max(0, parseInt(data.after, 10)), - end = start + 9; - - posts.getPostsFromSet('uid:' + data.uid + ':posts', socket.uid, start, end, callback); -}; + stop = start + 9; + posts.getPostsFromSet(set, uid, start, stop, callback); +} SocketPosts.getRecentPosts = function(socket, data, callback) { if(!data || !data.count) { diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index c270082953..d4bb12a993 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -204,6 +204,7 @@ SocketTopics.markAsUnreadForAll = function(socket, tids, callback) { return callback(err); } topics.pushUnreadCount(socket.uid); + callback(); }); }); }; @@ -470,11 +471,11 @@ SocketTopics.loadMore = function(socket, data, callback) { } } - var end = start + results.settings.postsPerPage - 1; + var stop = start + results.settings.postsPerPage - 1; async.parallel({ posts: function(next) { - topics.getTopicPosts(data.tid, set, start, end, socket.uid, reverse, next); + topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse, next); }, privileges: function(next) { next(null, results.privileges); @@ -490,33 +491,33 @@ SocketTopics.loadMore = function(socket, data, callback) { }; SocketTopics.loadMoreUnreadTopics = function(socket, data, callback) { - if(!data || !data.after) { + if (!data || !data.after) { return callback(new Error('[[error:invalid-data]]')); } var start = parseInt(data.after, 10), - end = start + 9; + stop = start + 9; - topics.getUnreadTopics(socket.uid, start, end, callback); + topics.getUnreadTopics(socket.uid, start, stop, callback); }; SocketTopics.loadMoreFromSet = function(socket, data, callback) { - if(!data || !data.after || !data.set) { + if (!data || !data.after || !data.set) { return callback(new Error('[[error:invalid-data]]')); } var start = parseInt(data.after, 10), - end = start + 9; + stop = start + 9; - topics.getTopicsFromSet(data.set, socket.uid, start, end, callback); + topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback); }; SocketTopics.loadTopics = function(socket, data, callback) { - if(!data || !data.set || !utils.isNumber(data.start) || !utils.isNumber(data.end)) { + if (!data || !data.set || !utils.isNumber(data.start) || !utils.isNumber(data.stop)) { return callback(new Error('[[error:invalid-data]]')); } - topics.getTopicsFromSet(data.set, socket.uid, data.start, data.end, callback); + topics.getTopicsFromSet(data.set, socket.uid, data.start, data.stop, callback); }; SocketTopics.getPageCount = function(socket, tid, callback) { @@ -544,14 +545,14 @@ SocketTopics.loadMoreTags = function(socket, data, callback) { } var start = parseInt(data.after, 10), - end = start + 99; + stop = start + 99; - topics.getTags(start, end, function(err, tags) { + topics.getTags(start, stop, function(err, tags) { if (err) { return callback(err); } - callback(null, {tags: tags, nextStart: end + 1}); + callback(null, {tags: tags, nextStart: stop + 1}); }); }; diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 807666ff18..0df36121e8 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -24,18 +24,27 @@ SocketUser.exists = function(socket, data, callback) { }; SocketUser.deleteAccount = function(socket, data, callback) { - if (socket.uid) { - user.isAdministrator(socket.uid, function(err, isAdmin) { - if (err || isAdmin) { - return callback(err || new Error('[[error:cant-delete-admin]]')); + if (!socket.uid) { + return; + } + user.isAdministrator(socket.uid, function(err, isAdmin) { + if (err || isAdmin) { + return callback(err || new Error('[[error:cant-delete-admin]]')); + } + + socket.broadcast.emit('event:user_status_change', {uid: socket.uid, status: 'offline'}); + user.deleteAccount(socket.uid, function(err) { + if (err) { + return callback(err); } - user.deleteAccount(socket.uid, callback); + websockets.in('uid_' + socket.uid).emit('event:logout'); + callback(); }); - } + }); }; SocketUser.emailExists = function(socket, data, callback) { - if(data && data.email) { + if (data && data.email) { user.email.exists(data.email, callback); } }; @@ -51,8 +60,7 @@ SocketUser.emailConfirm = function(socket, data, callback) { return; } - user.email.verify(socket.uid, email); - callback(); + user.email.sendValidationEmail(socket.uid, email, callback); }); } }; @@ -136,7 +144,7 @@ SocketUser.checkStatus = function(socket, uid, callback) { }; SocketUser.changePassword = function(socket, data, callback) { - if (!data || !data.uid) { + if (!data || !data.uid || data.newPassword.length < meta.config.minimumPasswordLength) { return callback(new Error('[[error:invalid-data]]')); } if (!socket.uid) { @@ -261,19 +269,27 @@ SocketUser.changePicture = function(socket, data, callback) { }); }; -SocketUser.uploadProfileImageFromUrl = function(socket, url, callback) { - if (!socket.uid || !url) { +SocketUser.uploadProfileImageFromUrl = function(socket, data, callback) { + function upload() { + user.uploadFromUrl(data.uid, data.url, function(err, uploadedImage) { + callback(err, uploadedImage ? uploadedImage.url : null); + }); + } + + if (!socket.uid || !data.url || !data.uid) { return; } - plugins.fireHook('filter:uploadImage', {image: {url: url}, uid: socket.uid}, function(err, data) { - if (err) { - return callback(err); + if (parseInt(socket.uid, 10) === parseInt(data.uid, 10)) { + return upload(); + } + + user.isAdministrator(socket.uid, function(err, isAdmin) { + if (err || !isAdmin) { + return callback(err || new Error('[[error:not-allowed]]')); } - user.setUserFields(socket.uid, {uploadedpicture: data.url, picture: data.url}, function(err) { - callback(err, data.url); - }); + upload(); }); }; @@ -397,7 +413,7 @@ SocketUser.getUnreadChatCount = function(socket, data, callback) { }; SocketUser.loadMore = function(socket, data, callback) { - if(!data || !data.set || parseInt(data.after, 10) < 0) { + if (!data || !data.set || parseInt(data.after, 10) < 0) { return callback(new Error('[[error:invalid-data]]')); } @@ -406,29 +422,32 @@ SocketUser.loadMore = function(socket, data, callback) { } var start = parseInt(data.after, 10), - end = start + 19; + stop = start + 19; - user.getUsersFromSet(data.set, socket.uid, start, end, function(err, userData) { + async.parallel({ + isAdmin: function(next) { + user.isAdministrator(socket.uid, next); + }, + users: function(next) { + user.getUsersFromSet(data.set, socket.uid, start, stop, next); + } + }, function(err, results) { if (err) { return callback(err); } - user.isAdministrator(socket.uid, function (err, isAdministrator) { - if (err) { - return callback(err); - } - if (!isAdministrator && data.set === 'users:online') { - userData = userData.filter(function(item) { - return item.status !== 'offline'; - }); - } - - callback(null, { - users: userData, - nextStart: end + 1 + if (!results.isAdmin && data.set === 'users:online') { + results.users = results.users.filter(function(user) { + return user.status !== 'offline'; }); - }); + } + var result = { + users: results.users, + nextStart: stop + 1, + }; + result['route_' + data.set] = true; + callback(null, result); }); }; diff --git a/src/threadTools.js b/src/threadTools.js index 76ea84c9df..c98e62dbcc 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -26,17 +26,18 @@ var async = require('async'), return callback(err); } - var alreadyDeletedOrRestored = (parseInt(topicData.deleted, 10) && isDelete) || (!parseInt(topicData.deleted, 10) && !isDelete); - if (alreadyDeletedOrRestored) { - return callback(null, {tid: tid}); + if (parseInt(topicData.deleted, 10) === 1 && isDelete) { + return callback(new Error('[[error:topic-already-deleted]]')); + } else if(parseInt(topicData.deleted, 10) !== 1 && !isDelete) { + return callback(new Error('[[error:topic-already-restored]]')); } topics[isDelete ? 'delete' : 'restore'](tid, function(err) { if (err) { return callback(err); } + topicData.deleted = isDelete ? 1 : 0; - ThreadTools[isDelete ? 'lock' : 'unlock'](tid, uid); if (isDelete) { plugins.fireHook('action:topic.delete', tid); } else { @@ -193,6 +194,7 @@ var async = require('async'), toCid: cid, uid: uid }); + callback(); }); }); }; diff --git a/src/topics.js b/src/topics.js index 0cc153e85e..c47b43bc45 100644 --- a/src/topics.js +++ b/src/topics.js @@ -112,16 +112,16 @@ var async = require('async'), }); }; - Topics.getTopicsFromSet = function(set, uid, start, end, callback) { + Topics.getTopicsFromSet = function(set, uid, start, stop, callback) { async.waterfall([ function(next) { - db.getSortedSetRevRange(set, start, end, next); + db.getSortedSetRevRange(set, start, stop, next); }, function(tids, next) { Topics.getTopics(tids, uid, next); }, function(topics, next) { - next(null, {topics: topics, nextStart: end + 1}); + next(null, {topics: topics, nextStart: stop + 1}); } ], callback); }; @@ -129,7 +129,7 @@ var async = require('async'), Topics.getTopics = function(tids, uid, callback) { async.waterfall([ function(next) { - privileges.topics.filter('read', tids, uid, next); + privileges.topics.filterTids('read', tids, uid, next); }, function(tids, next) { Topics.getTopicsByTids(tids, uid, next); @@ -209,14 +209,14 @@ var async = require('async'), }); }; - Topics.getTopicWithPosts = function(tid, set, uid, start, end, reverse, callback) { + Topics.getTopicWithPosts = function(tid, set, uid, start, stop, reverse, callback) { Topics.getTopicData(tid, function(err, topicData) { if (err || !topicData) { return callback(err || new Error('[[error:no-topic]]')); } async.parallel({ - posts: async.apply(getMainPostAndReplies, topicData, set, uid, start, end, reverse), + posts: async.apply(getMainPostAndReplies, topicData, set, uid, start, stop, reverse), category: async.apply(Topics.getCategoryData, tid), threadTools: async.apply(plugins.fireHook, 'filter:topic.thread_tools', {topic: topicData, uid: uid, tools: []}), tags: async.apply(Topics.getTopicTagsObjects, tid), @@ -244,10 +244,10 @@ var async = require('async'), }); }; - function getMainPostAndReplies(topic, set, uid, start, end, reverse, callback) { + function getMainPostAndReplies(topic, set, uid, start, stop, reverse, callback) { async.waterfall([ function(next) { - posts.getPidsFromSet(set, start, end, reverse, next); + posts.getPidsFromSet(set, start, stop, reverse, next); }, function(pids, next) { if ((!Array.isArray(pids) || !pids.length) && !topic.mainPid) { @@ -268,7 +268,7 @@ var async = require('async'), posts[0].index = 0; } - var indices = Topics.calculatePostIndices(start, end, topic.postcount, reverse); + var indices = Topics.calculatePostIndices(start, stop, topic.postcount, reverse); for (var i=1; i meta.config.maximumUsernameLength) { + return false; + } + return true; + } + }; diff --git a/src/topics/delete.js b/src/topics/delete.js index 1bb8e0798c..47cb384214 100644 --- a/src/topics/delete.js +++ b/src/topics/delete.js @@ -4,24 +4,39 @@ var async = require('async'), db = require('../database'), user = require('../user'), + posts = require('../posts'), plugins = require('../plugins'); module.exports = function(Topics) { Topics.delete = function(tid, callback) { - async.parallel([ - function(next) { - Topics.setTopicField(tid, 'deleted', 1, next); - }, - function(next) { - db.sortedSetsRemove(['topics:recent', 'topics:posts', 'topics:views'], tid, next); + Topics.getTopicFields(tid, ['cid'], function(err, topicData) { + if (err) { + return callback(err); } - ], callback); + + async.parallel([ + function(next) { + Topics.setTopicField(tid, 'deleted', 1, next); + }, + function(next) { + db.sortedSetsRemove(['topics:recent', 'topics:posts', 'topics:views'], tid, next); + }, + function(next) { + Topics.getPids(tid, function(err, pids) { + if (err) { + return next(err); + } + db.sortedSetRemove('cid:' + topicData.cid + ':pids', pids, next); + }); + } + ], callback); + }); }; Topics.restore = function(tid, callback) { - Topics.getTopicFields(tid, ['lastposttime', 'postcount', 'viewcount'], function(err, topicData) { + Topics.getTopicFields(tid, ['cid', 'lastposttime', 'postcount', 'viewcount'], function(err, topicData) { if (err) { return callback(err); } @@ -38,6 +53,28 @@ module.exports = function(Topics) { }, function(next) { db.sortedSetAdd('topics:views', topicData.viewcount, tid, next); + }, + function(next) { + Topics.getPids(tid, function(err, pids) { + if (err) { + return callback(err); + } + + posts.getPostsFields(pids, ['pid', 'timestamp', 'deleted'], function(err, postData) { + if (err) { + return next(err); + } + postData = postData.filter(function(post) { + return post && parseInt(post.deleted, 10) !== 1; + }); + var pidsToAdd = [], scores = []; + postData.forEach(function(post) { + pidsToAdd.push(post.pid); + scores.push(post.timestamp); + }); + db.sortedSetAdd('cid:' + topicData.cid + ':pids', scores, pidsToAdd, next); + }); + }); } ], callback); }); diff --git a/src/topics/follow.js b/src/topics/follow.js index 7bcfab8ea7..961873ea10 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -11,6 +11,7 @@ var async = require('async'), posts = require('../posts'), postTools = require('../postTools'), notifications = require('../notifications'), + privileges = require('../privileges'), meta = require('../meta'), emailer = require('../emailer'); @@ -96,60 +97,80 @@ module.exports = function(Topics) { db.getSetMembers('tid:' + tid + ':followers', callback); }; - Topics.notifyFollowers = function(postData, exceptUid) { - Topics.getFollowers(postData.topic.tid, function(err, followers) { - if (err || !Array.isArray(followers) || !followers.length) { - return; - } - - var index = followers.indexOf(exceptUid.toString()); - if (index !== -1) { - followers.splice(index, 1); - } - - if (!followers.length) { - return; - } + Topics.notifyFollowers = function(postData, exceptUid, callback) { + callback = callback || function() {}; + var followers, title; - var title = postData.topic.title; - if (title) { - title = S(title).decodeHTMLEntities().s; - } + async.waterfall([ + function(next) { + Topics.getFollowers(postData.topic.tid, next); + }, + function(followers, next) { + if (!Array.isArray(followers) || !followers.length) { + return callback(); + } + var index = followers.indexOf(exceptUid.toString()); + if (index !== -1) { + followers.splice(index, 1); + } + if (!followers.length) { + return callback(); + } - notifications.create({ - bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + title + ']]', - bodyLong: postData.content, - pid: postData.pid, - nid: 'tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, - tid: postData.topic.tid, - from: exceptUid - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, followers); + privileges.topics.filterUids('read', postData.topic.tid, followers, next); + }, + function(_followers, next) { + followers = _followers; + if (!followers.length) { + return callback(); + } + title = postData.topic.title; + if (title) { + title = S(title).decodeHTMLEntities().s; } - }); - async.eachLimit(followers, 3, function(toUid, next) { - async.parallel({ - userData: async.apply(user.getUserFields, toUid, ['username']), - userSettings: async.apply(user.getSettings, toUid) - }, function(err, data) { - if (data.userSettings.hasOwnProperty('sendPostNotifications') && data.userSettings.sendPostNotifications) { - emailer.send('notif_post', toUid, { - pid: postData.pid, - subject: '[' + (meta.config.title || 'NodeBB') + '] ' + title, - intro: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + title + ']]', - postBody: postData.content, - site_title: meta.config.title || 'NodeBB', - username: data.userData.username, - url: nconf.get('url') + '/topic/' + postData.topic.tid, - base_url: nconf.get('url') - }, next); - } else { - winston.debug('[topics.notifyFollowers] uid ' + toUid + ' does not have post notifications enabled, skipping.'); + notifications.create({ + bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + title + ']]', + bodyLong: postData.content, + pid: postData.pid, + nid: 'tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, + tid: postData.topic.tid, + from: exceptUid + }, function(err, notification) { + if (err) { + return next(err); + } + if (notification) { + notifications.push(notification, followers); } + next(); }); - }); - }); + }, + function(next) { + async.eachLimit(followers, 3, function(toUid, next) { + async.parallel({ + userData: async.apply(user.getUserFields, toUid, ['username', 'userslug']), + userSettings: async.apply(user.getSettings, toUid) + }, function(err, data) { + if (data.userSettings.hasOwnProperty('sendPostNotifications') && data.userSettings.sendPostNotifications) { + emailer.send('notif_post', toUid, { + pid: postData.pid, + subject: '[' + (meta.config.title || 'NodeBB') + '] ' + title, + intro: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + title + ']]', + postBody: postData.content, + site_title: meta.config.title || 'NodeBB', + username: data.userData.username, + userslug: data.userData.userslug, + url: nconf.get('url') + '/topic/' + postData.topic.tid, + base_url: nconf.get('url') + }, next); + } else { + winston.debug('[topics.notifyFollowers] uid ' + toUid + ' does not have post notifications enabled, skipping.'); + } + }); + }); + next(); + } + ], callback); }; -}; \ No newline at end of file +}; diff --git a/src/topics/popular.js b/src/topics/popular.js index a1149914fc..88d5a6819a 100644 --- a/src/topics/popular.js +++ b/src/topics/popular.js @@ -44,7 +44,7 @@ module.exports = function(Topics) { }).slice(0, count).map(function(topic) { return topic.tid; }); - privileges.topics.filter('read', tids, uid, next); + privileges.topics.filterTids('read', tids, uid, next); }, function(tids, next) { Topics.getTopicsByTids(tids, uid, next); diff --git a/src/topics/posts.js b/src/topics/posts.js index 6d4cd9be8f..e895556145 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -5,6 +5,7 @@ var async = require('async'), winston = require('winston'), _ = require('underscore'), + validator = require('validator'), db = require('../database'), user = require('../user'), @@ -29,11 +30,11 @@ module.exports = function(Topics) { ], callback); }; - Topics.getTopicPosts = function(tid, set, start, end, uid, reverse, callback) { + Topics.getTopicPosts = function(tid, set, start, stop, uid, reverse, callback) { callback = callback || function() {}; async.parallel({ posts: function(next) { - posts.getPostsByTid(tid, set, start, end, uid, reverse, next); + posts.getPostsByTid(tid, set, start, stop, uid, reverse, next); }, postCount: function(next) { Topics.getTopicField(tid, 'postcount', next); @@ -43,7 +44,7 @@ module.exports = function(Topics) { return callback(err); } - var indices = Topics.calculatePostIndices(start, end, results.postCount, reverse); + var indices = Topics.calculatePostIndices(start, stop, results.postCount, reverse); results.posts.forEach(function(post, index) { if (post) { post.index = indices[index]; @@ -141,7 +142,7 @@ module.exports = function(Topics) { // Username override for guests, if enabled if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postObj.uid, 10) === 0 && postObj.handle) { - postObj.user.username = postObj.handle; + postObj.user.username = validator.escape(postObj.handle); } } }); @@ -150,9 +151,9 @@ module.exports = function(Topics) { }); }; - Topics.calculatePostIndices = function(start, end, postCount, reverse) { + Topics.calculatePostIndices = function(start, stop, postCount, reverse) { var indices = []; - var count = end - start + 1; + var count = stop - start + 1; for(var i=0; i b; }); - + plugins.fireHook('filter:tags.search', {data: data, matches: matches}, function(err, data) { callback(err, data ? data.matches : []); }); diff --git a/src/topics/teaser.js b/src/topics/teaser.js index 5946eef988..4574151554 100644 --- a/src/topics/teaser.js +++ b/src/topics/teaser.js @@ -9,7 +9,6 @@ var async = require('async'), user = require('../user'), posts = require('../posts'), plugins = require('../plugins'), - postTools = require('../postTools'), utils = require('../../public/src/utils'); @@ -56,7 +55,7 @@ module.exports = function(Topics) { post.user = users[post.uid]; post.timestamp = utils.toISOString(post.timestamp); tidToPost[post.tid] = post; - postTools.parsePost(post, next); + posts.parsePost(post, next); }, function(err) { if (err) { return callback(err); @@ -101,11 +100,12 @@ module.exports = function(Topics) { }; Topics.updateTeaser = function(tid, callback) { - db.getSortedSetRevRange('tid:' + tid + ':posts', 0, 0, function(err, pids) { + Topics.getLatestUndeletedReply(tid, function(err, pid) { if (err) { return callback(err); } - var pid = Array.isArray(pids) && pids.length ? pids[0] : null; + + pid = pid || null; Topics.setTopicField(tid, 'teaserPid', pid, callback); }); }; diff --git a/src/topics/unread.js b/src/topics/unread.js index b9392d3eda..53f804f9fc 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -114,7 +114,7 @@ module.exports = function(Topics) { async.waterfall([ function(next) { - privileges.topics.filter('read', tids, uid, next); + privileges.topics.filterTids('read', tids, uid, next); }, function(tids, next) { Topics.getTopicsFields(tids, ['tid', 'cid'], next); diff --git a/src/upgrade.js b/src/upgrade.js index 3a52020ca5..5b7c8f65ed 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -25,18 +25,21 @@ var db = require('./database'), Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { - if(!value) { + if (err) { + return callback(err); + } + + if (!value) { db.set('schemaDate', latestSchema, function(err) { - callback(true); + if (err) { + return callback(err); + } + callback(null, true); }); return; } - if (parseInt(value, 10) >= latestSchema) { - callback(true); - } else { - callback(false); - } + callback(null, parseInt(value, 10) >= latestSchema); }); }; @@ -971,23 +974,25 @@ Upgrade.upgrade = function(callback) { } else { winston.info('[upgrade] Schema already up to date!'); } - - process.exit(); } else { switch(err.message) { case 'upgrade-not-possible': winston.error('[upgrade] NodeBB upgrade could not complete, as your database schema is too far out of date.'); winston.error('[upgrade] Please ensure that you did not skip any minor version upgrades.'); winston.error('[upgrade] (e.g. v0.1.x directly to v0.3.x)'); - process.exit(); break; default: winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message); - process.exit(); break; } } + + if (typeof callback === 'function') { + callback(err); + } else { + process.exit(); + } }); }; diff --git a/src/user.js b/src/user.js index 86e0d904be..7ad9810554 100644 --- a/src/user.js +++ b/src/user.js @@ -9,7 +9,8 @@ var async = require('async'), meta = require('./meta'), topics = require('./topics'), groups = require('./groups'), - Password = require('./password'); + Password = require('./password'), + utils = require('../public/src/utils'); (function(User) { @@ -28,6 +29,7 @@ var async = require('async'), require('./user/settings')(User); require('./user/search')(User); require('./user/jobs')(User); + require('./user/picture')(User); User.getUserField = function(uid, field, callback) { User.getUserFields(uid, [field], function(err, user) { @@ -240,7 +242,7 @@ var async = require('async'), }; User.getUsers = function(uids, uid, callback) { - var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'postcount', 'reputation', 'email:confirmed']; + var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'joindate', 'postcount', 'reputation', 'email:confirmed']; plugins.fireHook('filter:users.addFields', {fields: fields}, function(err, data) { if (err) { return callback(err); @@ -268,6 +270,7 @@ var async = require('async'), return; } user.status = User.getStatus(user.status, results.isOnline[index]); + user.joindateISO = utils.toISOString(user.joindate); user.administrator = results.isAdmin[index]; user.banned = parseInt(user.banned, 10) === 1; user['email:confirmed'] = parseInt(user['email:confirmed'], 10) === 1; @@ -430,7 +433,7 @@ var async = require('async'), } var isMembers = checks.user.map(function(isMember, idx) { - return isMember || checks.group[idx] + return isMember || checks.group[idx]; }), map = {}; @@ -449,7 +452,7 @@ var async = require('async'), async.apply(groups.isMembers, uid, 'cid:' + cid + ':privileges:groups:moderate') ], function(err, checks) { var isModerator = checks[0].map(function(isMember, idx) { - return isMember || checks[1][idx] + return isMember || checks[1][idx]; }); filterIsModerator(null, isModerator); }); diff --git a/src/user/admin.js b/src/user/admin.js index 4693fe539d..4d559fcb34 100644 --- a/src/user/admin.js +++ b/src/user/admin.js @@ -15,8 +15,8 @@ module.exports = function(User) { } }; - User.getIPs = function(uid, end, callback) { - db.getSortedSetRevRange('uid:' + uid + ':ip', 0, end, function(err, ips) { + User.getIPs = function(uid, stop, callback) { + db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, function(err, ips) { if(err) { return callback(err); } diff --git a/src/user/create.js b/src/user/create.js index 90bd39e7d1..db85c81508 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -116,7 +116,7 @@ module.exports = function(User) { if (userData.email) { db.setObjectField('email:uid', userData.email.toLowerCase(), userData.uid, next); if (parseInt(userData.uid, 10) !== 1 && parseInt(meta.config.requireEmailConfirmation, 10) === 1) { - User.email.verify(userData.uid, userData.email); + User.email.sendValidationEmail(userData.uid, userData.email); } } else { next(); @@ -126,12 +126,16 @@ module.exports = function(User) { if (!data.password) { return next(); } + User.hashPassword(data.password, function(err, hash) { if (err) { return next(err); } - User.setUserField(userData.uid, 'password', hash, next); + async.parallel([ + async.apply(User.setUserField, userData.uid, 'password', hash), + async.apply(User.reset.updateExpiry, userData.uid) + ], next); }); } ], function(err) { diff --git a/src/user/delete.js b/src/user/delete.js index 1c1e9c2974..c1d67bd92f 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -131,16 +131,39 @@ module.exports = function(User) { } function deleteUserFromFollowers(uid, callback) { - db.getSetMembers('followers:' + uid, function(err, uids) { + async.parallel({ + followers: async.apply(db.getSortedSetRange, 'followers:' + uid, 0, -1), + following: async.apply(db.getSortedSetRange, 'following:' + uid, 0, -1) + }, function(err, results) { + function updateCount(uids, name, fieldName, next) { + async.each(uids, function(uid, next) { + db.sortedSetCard(name + uid, function(err, count) { + if (err) { + return next(err); + } + count = parseInt(count, 10) || 0; + db.setObjectField('user:' + uid, fieldName, count, next); + }); + }, next); + } + if (err) { return callback(err); } - var sets = uids.map(function(uid) { + var followingSets = results.followers.map(function(uid) { return 'following:' + uid; }); - db.setsRemove(sets, uid, callback); + var followerSets = results.following.map(function(uid) { + return 'followers:' + uid; + }); + + async.parallel([ + async.apply(db.sortedSetsRemove, followerSets.concat(followingSets), uid), + async.apply(updateCount, results.following, 'followers:', 'followerCount'), + async.apply(updateCount, results.followers, 'following:', 'followingCount') + ], callback); }); } }; diff --git a/src/user/digest.js b/src/user/digest.js index 02799c0bf3..97b408b910 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -9,6 +9,7 @@ var async = require('async'), user = require('../user'), topics = require('../topics'), batch = require('../batch'), + plugins = require('../plugins'), emailer = require('../emailer'), utils = require('../../public/src/utils'); @@ -19,6 +20,10 @@ var async = require('async'), return winston.verbose('[user/jobs] Did not send digests (' + interval + ') because subscription system is disabled.'); } + if (!plugins.hasListeners('action:email.send')) { + return winston.error('[user/jobs] Did not send digests (' + interval + ') because no active email plugin was found.'); + } + if (!interval) { // interval is one of: day, week, month, or year interval = 'day'; diff --git a/src/user/email.js b/src/user/email.js index 973a7e844e..b4c6460683 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -27,61 +27,68 @@ var async = require('async'), }); }; - UserEmail.verify = function(uid, email, callback) { + UserEmail.sendValidationEmail = function(uid, email, callback) { callback = callback || function() {}; var confirm_code = utils.generateUUID(), confirm_link = nconf.get('url') + '/confirm/' + confirm_code; - plugins.fireHook('filter:user.verify.code', confirm_code, function(err, confirm_code) { - if (err) { - return callback(err); - } + var emailInterval = 10; - async.series([ - function(next) { - db.setObject('confirm:' + confirm_code, { - email: email.toLowerCase(), - uid: uid - }, next); - }, - function(next) { - db.expireAt('confirm:' + confirm_code, Math.floor(Date.now() / 1000 + 60 * 60 * 2), next); + async.waterfall([ + function(next) { + db.get('uid:' + uid + ':confirm:email:sent', next); + }, + function(sent, next) { + if (sent) { + return next(new Error('[[error:confirm-email-already-sent, ' + emailInterval + ']]')); } - ], function(err) { - if (err) { - return callback(err); - } - user.getUserField(uid, 'username', function(err, username) { - if (err) { - return winston.error(err.stack); - } + db.set('uid:' + uid + ':confirm:email:sent', 1, next); + }, + function(next) { + db.pexpireAt('uid:' + uid + ':confirm:email:sent', Date.now() + (emailInterval * 60 * 1000), next); + }, + function(next) { + plugins.fireHook('filter:user.verify.code', confirm_code, next); + }, + function(_confirm_code, next) { + confirm_code = _confirm_code; + db.setObject('confirm:' + confirm_code, { + email: email.toLowerCase(), + uid: uid + }, next); + }, + function(next) { + db.expireAt('confirm:' + confirm_code, Math.floor(Date.now() / 1000 + 60 * 60 * 2), next); + }, + function(next) { + user.getUserField(uid, 'username', next); + }, + function(username, next) { + var title = meta.config.title || meta.config.browserTitle || 'NodeBB'; + translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function(subject) { + var data = { + site_title: title, + username: username, + confirm_link: confirm_link, + confirm_code: confirm_code, - var title = meta.config.title || meta.config.browserTitle || 'NodeBB'; - translator.translate('[[email:welcome-to, ' + title + ']]', meta.config.defaultLang, function(subject) { - var data = { - site_title: title, - username: username, - confirm_link: confirm_link, - confirm_code: confirm_code, - - subject: subject, - template: 'welcome', - uid: uid - }; + subject: subject, + template: 'welcome', + uid: uid + }; - if (plugins.hasListeners('action:user.verify')) { - plugins.fireHook('action:user.verify', {uid: uid, data: data}); - callback(); - } else if (plugins.hasListeners('action:email.send')) { - emailer.send('welcome', uid, data, callback); - } else { - winston.warn('No emailer to send verification email!'); - callback(); - } - }); + if (plugins.hasListeners('action:user.verify')) { + plugins.fireHook('action:user.verify', {uid: uid, data: data}); + next(); + } else if (plugins.hasListeners('action:email.send')) { + emailer.send('welcome', uid, data, next); + } else { + winston.warn('No emailer to send verification email!'); + next(); + } }); - }); - }); + } + ], callback); }; UserEmail.confirm = function(code, callback) { diff --git a/src/user/follow.js b/src/user/follow.js index b7bda96903..fc776bcef8 100644 --- a/src/user/follow.js +++ b/src/user/follow.js @@ -53,20 +53,20 @@ module.exports = function(User) { }); } - User.getFollowing = function(uid, start, end, callback) { - getFollow(uid, 'following:' + uid, start, end, callback); + User.getFollowing = function(uid, start, stop, callback) { + getFollow(uid, 'following:' + uid, start, stop, callback); }; - User.getFollowers = function(uid, start, end, callback) { - getFollow(uid, 'followers:' + uid, start, end, callback); + User.getFollowers = function(uid, start, stop, callback) { + getFollow(uid, 'followers:' + uid, start, stop, callback); }; - function getFollow(uid, set, start, end, callback) { + function getFollow(uid, set, start, stop, callback) { if (!parseInt(uid, 10)) { return callback(null, []); } - db.getSortedSetRevRange(set, start, end, function(err, uids) { + db.getSortedSetRevRange(set, start, stop, function(err, uids) { if (err) { return callback(err); } diff --git a/src/user/picture.js b/src/user/picture.js new file mode 100644 index 0000000000..8f030091a9 --- /dev/null +++ b/src/user/picture.js @@ -0,0 +1,133 @@ +'use strict'; + +var async = require('async'), + path = require('path'), + fs = require('fs'), + nconf = require('nconf'), + winston = require('winston'), + request = require('request'), + mime = require('mime'), + + plugins = require('../plugins'), + file = require('../file'), + image = require('../image'), + meta = require('../meta'); + +module.exports = function(User) { + + User.uploadPicture = function (uid, picture, callback) { + + var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; + var extension = path.extname(picture.name); + var updateUid = uid; + var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; + var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; + + async.waterfall([ + function(next) { + next(parseInt(meta.config.allowProfileImageUploads) !== 1 ? new Error('[[error:profile-image-uploads-disabled]]') : null); + }, + function(next) { + next(picture.size > uploadSize * 1024 ? new Error('[[error:file-too-big, ' + uploadSize + ']]') : null); + }, + function(next) { + next(!extension ? new Error('[[error:invalid-image-extension]]') : null); + }, + function(next) { + file.isFileTypeAllowed(picture.path, ['png', 'jpeg', 'jpg', 'gif'], next); + }, + function(next) { + image.resizeImage(picture.path, extension, imageDimension, imageDimension, next); + }, + function(next) { + if (convertToPNG) { + image.convertImageToPng(picture.path, extension, next); + } else { + next(); + } + } + ], function(err, result) { + function done(err, image) { + if (err) { + return callback(err); + } + + User.setUserFields(updateUid, {uploadedpicture: image.url, picture: image.url}); + + callback(null, image); + } + + if (err) { + return callback(err); + } + + if (plugins.hasListeners('filter:uploadImage')) { + return plugins.fireHook('filter:uploadImage', {image: picture, uid: updateUid}, done); + } + + var filename = updateUid + '-profileimg' + (convertToPNG ? '.png' : extension); + + User.getUserField(updateUid, 'uploadedpicture', function (err, oldpicture) { + if (err) { + return callback(err); + } + + if (!oldpicture) { + return file.saveFileToLocal(filename, 'profile', picture.path, done); + } + + var absolutePath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), 'profile', path.basename(oldpicture)); + + fs.unlink(absolutePath, function (err) { + if (err) { + winston.error(err); + } + + file.saveFileToLocal(filename, 'profile', picture.path, done); + }); + }); + }); + }; + + User.uploadFromUrl = function(uid, url, callback) { + var filename = 'uid:' + uid + ':tmp-image'; + downloadFromUrl(url, filename, function(err, downloadedImage) { + if (err) { + return callback(err); + } + + User.uploadPicture(uid, downloadedImage, function(err, image) { + fs.unlink(filename); + callback(err, image); + }); + }); + }; + + function downloadFromUrl(url, filename, callback) { + request.head(url, function(err, res, body) { + if (err) { + return callback(err); + } + var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; + var size = res.headers['content-length']; + var type = res.headers['content-type']; + var extension = mime.extension(type); + + if (size > uploadSize * 1024) { + return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); + } + + request.get(url) + .on('error', function(err) { + winston.error(err); + }) + .pipe(fs.createWriteStream(filename)) + .on('close', function(err) { + if (err) { + return callback(err); + } + callback(null, {path: filename, size: size, type: type, name: filename + '.' + extension}); + }); + }); + } +}; \ No newline at end of file diff --git a/src/user/profile.js b/src/user/profile.js index f16046ee4a..7265d5896b 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -3,6 +3,7 @@ var async = require('async'), validator = require('validator'), + url = require('url'), S = require('string'), utils = require('../../public/src/utils'), @@ -15,7 +16,7 @@ var async = require('async'), module.exports = function(User) { User.updateProfile = function(uid, data, callback) { - var fields = ['username', 'email', 'fullname', 'website', 'location', 'birthday', 'signature']; + var fields = ['username', 'email', 'fullname', 'website', 'location', 'birthday', 'signature', 'aboutme']; plugins.fireHook('filter:user.updateProfile', {uid: uid, data: data, fields: fields}, function(err, data) { if (err) { @@ -25,6 +26,14 @@ module.exports = function(User) { fields = data.fields; data = data.data; + function isAboutMeValid(next) { + if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) { + next(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]')); + } else { + next(); + } + } + function isSignatureValid(next) { if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) { next(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]')); @@ -91,7 +100,7 @@ module.exports = function(User) { }); } - async.series([isSignatureValid, isEmailAvailable, isUsernameAvailable], function(err, results) { + async.series([isAboutMeValid, isSignatureValid, isEmailAvailable, isUsernameAvailable], function(err) { if (err) { return callback(err); } @@ -111,7 +120,6 @@ module.exports = function(User) { } data[field] = data[field].trim(); - data[field] = validator.escape(data[field]); if (field === 'email') { return updateEmail(uid, data.email, next); @@ -122,9 +130,22 @@ module.exports = function(User) { } else if (field === 'signature') { data[field] = S(data[field]).stripTags().s; } else if (field === 'website') { - if (!data[field].startsWith(validator.escape('http://')) && !data[field].startsWith(validator.escape('https://'))) { - data[field] = validator.escape('http://') + data[field]; + if (data[field].length > 0) { + var urlObj = url.parse(data[field], false, true); + if (!urlObj.protocol) { + urlObj.protocol = 'http'; + urlObj.slashes = true; + } + if (!urlObj.hostname && urlObj.pathname) { + urlObj.hostname = urlObj.pathname; + urlObj.pathname = null; + } + if (urlObj.pathname === '/') { + urlObj.pathname = null; + } } + + data[field] = url.format(urlObj); } User.setUserField(uid, field, data[field], next); @@ -162,7 +183,7 @@ module.exports = function(User) { }, function(next) { if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && newEmail) { - User.email.verify(uid, newEmail); + User.email.sendValidationEmail(uid, newEmail); } User.setUserField(uid, 'email:confirmed', 0, next); }, @@ -257,7 +278,10 @@ module.exports = function(User) { return callback(err); } - User.setUserField(data.uid, 'password', hash, callback); + async.parallel([ + async.apply(User.setUserField, data.uid, 'password', hash), + async.apply(User.reset.updateExpiry, data.uid) + ], callback); }); } diff --git a/src/user/reset.js b/src/user/reset.js index 9df72a8177..e9a5e46c1a 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -32,8 +32,17 @@ var async = require('async'), ], callback); }; + UserReset.generate = function(uid, callback) { + var code = utils.generateUUID(); + async.parallel([ + async.apply(db.setObjectField, 'reset:uid', code, uid), + async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code) + ], function(err) { + callback(err, code); + }); + }; + UserReset.send = function(email, callback) { - var reset_code = utils.generateUUID(); var uid; async.waterfall([ function(next) { @@ -45,18 +54,15 @@ var async = require('async'), } uid = _uid; - async.parallel([ - async.apply(db.setObjectField, 'reset:uid', reset_code, uid), - async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), reset_code) - ], next); + UserReset.generate(uid, next); }, - function(results, next) { + function(code, next) { translator.translate('[[email:password-reset-requested, ' + (meta.config.title || 'NodeBB') + ']]', meta.config.defaultLang, function(subject) { - next(null, subject); + next(null, subject, code); }); }, - function(subject, next) { - var reset_link = nconf.get('url') + '/reset/' + reset_code; + function(subject, code, next) { + var reset_link = nconf.get('url') + '/reset/' + code; emailer.send('reset', uid, { site_title: (meta.config.title || 'NodeBB'), reset_link: reset_link, @@ -64,9 +70,6 @@ var async = require('async'), template: 'reset', uid: uid }, next); - }, - function(next) { - next(null, reset_code); } ], callback); }; @@ -96,12 +99,22 @@ var async = require('async'), async.apply(user.setUserField, uid, 'password', hash), async.apply(db.deleteObjectField, 'reset:uid', code), async.apply(db.sortedSetRemove, 'reset:issueDate', code), + async.apply(user.reset.updateExpiry, uid), async.apply(user.auth.resetLockout, uid) ], next); } ], callback); }; + UserReset.updateExpiry = function(uid, callback) { + var oneDay = 1000*60*60*24, + expireDays = parseInt(meta.config.passwordExpiryDays || 0, 10), + expiry = Date.now() + (oneDay * expireDays); + + callback = callback || function() {}; + user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback); + }; + UserReset.clean = function(callback) { async.waterfall([ async.apply(db.getSortedSetRangeByScore, 'reset:issueDate', 0, -1, 0, Date.now() - twoHours), diff --git a/src/user/search.js b/src/user/search.js index 7545a6d3f3..ec1420e676 100644 --- a/src/user/search.js +++ b/src/user/search.js @@ -43,6 +43,7 @@ module.exports = function(User) { var pagination = user.paginate(page, uids); uids = pagination.data; searchResult.pagination = pagination.pagination; + searchResult.pageCount = pagination.pageCount; } User.getUsers(uids, uid, next); @@ -59,14 +60,15 @@ module.exports = function(User) { User.paginate = function(page, data) { var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 20; var start = Math.max(0, page - 1) * resultsPerPage; - var end = start + resultsPerPage; + var stop = start + resultsPerPage; var pageCount = Math.ceil(data.length / resultsPerPage); var currentPage = Math.max(1, Math.ceil((start + 1) / resultsPerPage)); return { pagination: pagination.create(currentPage, pageCount), - data: data.slice(start, end) + pageCount: pageCount, + data: data.slice(start, stop) }; }; diff --git a/src/user/settings.js b/src/user/settings.js index 20c16fceb8..7c12cbfd00 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -55,13 +55,16 @@ module.exports = function(User) { settings = data.settings; + var defaultTopicsPerPage = parseInt(meta.config.topicsPerPage, 10) || 20; + var defaultPostsPerPage = parseInt(meta.config.postsPerPage, 10) || 20; + settings.showemail = parseInt(settings.showemail, 10) === 1; settings.showfullname = parseInt(settings.showfullname, 10) === 1; settings.openOutgoingLinksInNewTab = parseInt(settings.openOutgoingLinksInNewTab, 10) === 1; settings.dailyDigestFreq = settings.dailyDigestFreq || 'off'; settings.usePagination = (settings.usePagination === null || settings.usePagination === undefined) ? parseInt(meta.config.usePagination, 10) === 1 : parseInt(settings.usePagination, 10) === 1; - settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : parseInt(meta.config.topicsPerPage, 10) || 20, 20); - settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : parseInt(meta.config.postsPerPage, 10) || 10, 20); + settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); + settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); settings.notificationSounds = parseInt(settings.notificationSounds, 10) === 1; settings.userLang = settings.userLang || meta.config.defaultLang || 'en_GB'; settings.topicPostSort = settings.topicPostSort || meta.config.topicPostSort || 'oldest_to_newest'; @@ -78,7 +81,7 @@ module.exports = function(User) { } User.saveSettings = function(uid, data, callback) { - if(!data.topicsPerPage || !data.postsPerPage || parseInt(data.topicsPerPage, 10) <= 0 || parseInt(data.postsPerPage, 10) <= 0) { + if (invalidPaginationSettings(data)) { return callback(new Error('[[error:invalid-pagination-value]]')); } @@ -94,8 +97,8 @@ module.exports = function(User) { openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, dailyDigestFreq: data.dailyDigestFreq || 'off', usePagination: data.usePagination, - topicsPerPage: Math.min(data.topicsPerPage, 20), - postsPerPage: Math.min(data.postsPerPage, 20), + topicsPerPage: Math.min(data.topicsPerPage, parseInt(meta.config.topicsPerPage, 10) || 20), + postsPerPage: Math.min(data.postsPerPage, parseInt(meta.config.postsPerPage, 10) || 20), notificationSounds: data.notificationSounds, userLang: data.userLang || meta.config.defaultLang, followTopicsOnCreate: data.followTopicsOnCreate, @@ -116,6 +119,12 @@ module.exports = function(User) { ], callback); }; + function invalidPaginationSettings(data) { + return !data.topicsPerPage || !data.postsPerPage || + parseInt(data.topicsPerPage, 10) <= 0 || parseInt(data.postsPerPage, 10) <= 0 || + parseInt(data.topicsPerPage, 10) > meta.config.topicsPerPage || parseInt(data.postsPerPage, 10) > meta.config.postsPerPage; + } + function updateDigestSetting(uid, dailyDigestFreq, callback) { async.waterfall([ function(next) { diff --git a/src/views/admin/advanced/post-cache.tpl b/src/views/admin/advanced/post-cache.tpl new file mode 100644 index 0000000000..7f688327e4 --- /dev/null +++ b/src/views/admin/advanced/post-cache.tpl @@ -0,0 +1,27 @@ + +
    +
    +
    +
    Post Cache
    +
    + +
    + {cache.itemCount}
    + +
    + {cache.avgPostSize}
    + +
    + {cache.length} / {cache.max}
    + +
    +
    + {cache.percentFull}% Full +
    +
    + +
    +
    +
    + +
    diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl index db18b7cf02..132797fb3e 100644 --- a/src/views/admin/general/dashboard.tpl +++ b/src/views/admin/general/dashboard.tpl @@ -19,6 +19,10 @@
    Page views This Month
    +
    +
    +
    Page views in last 24 hours
    +
    @@ -111,7 +115,6 @@
  • Reading posts
  • Browsing topics
  • Recent / Unread
  • -
  • Tags
  • diff --git a/src/views/admin/general/languages.tpl b/src/views/admin/general/languages.tpl index b2365e91a3..6075647d77 100644 --- a/src/views/admin/general/languages.tpl +++ b/src/views/admin/general/languages.tpl @@ -14,13 +14,13 @@ - + @@ -35,6 +35,4 @@ - + diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl index a2b3a4c8e7..52f12d0e0c 100644 --- a/src/views/admin/header.tpl +++ b/src/views/admin/header.tpl @@ -74,7 +74,32 @@ diff --git a/src/views/admin/manage/categories.tpl b/src/views/admin/manage/categories.tpl index 69526e86b4..39d2a15c9b 100644 --- a/src/views/admin/manage/categories.tpl +++ b/src/views/admin/manage/categories.tpl @@ -1,7 +1,7 @@
    -
    Categories
    +
    Active Categories
    @@ -55,7 +55,7 @@
    -
    Categories
    +
    Disabled Categories
    diff --git a/src/views/admin/partials/categories/privileges.tpl b/src/views/admin/partials/categories/privileges.tpl index b6b33ac42b..d18329753e 100644 --- a/src/views/admin/partials/categories/privileges.tpl +++ b/src/views/admin/partials/categories/privileges.tpl @@ -42,4 +42,10 @@
    +
    + 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. +
    \ No newline at end of file diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index dcf28ba45e..c217d99972 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -58,6 +58,7 @@
  • Database
  • Events
  • Logs
  • +
  • Post Cache
  • diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl index 9b7f190498..716cdbf56d 100644 --- a/src/views/admin/settings/tags.tpl +++ b/src/views/admin/settings/tags.tpl @@ -6,7 +6,7 @@
    diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index c0c1ca8ab3..c2832c530a 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -46,6 +46,12 @@
    Avatars
    +
    + +
    +
    +
    + + +
    @@ -122,6 +132,10 @@
    +
    + + +
    diff --git a/src/views/emails/notif_post.tpl b/src/views/emails/notif_post.tpl index 1f91d044be..78ac8fefb0 100644 --- a/src/views/emails/notif_post.tpl +++ b/src/views/emails/notif_post.tpl @@ -12,5 +12,5 @@

    - [[email:notif.post.unsub.info]] [[email:unsub.cta]]. -

    \ No newline at end of file + [[email:notif.post.unsub.info]] [[email:unsub.cta]]. +

    diff --git a/src/views/emails/notif_post_plaintext.tpl b/src/views/emails/notif_post_plaintext.tpl index 0920b2f79e..0a317466a8 100644 --- a/src/views/emails/notif_post_plaintext.tpl +++ b/src/views/emails/notif_post_plaintext.tpl @@ -11,4 +11,4 @@ === -[[email:notif.post.unsub.info]] [[email:unsub.cta]]. \ No newline at end of file +[[email:notif.post.unsub.info]] [[email:unsub.cta]]. diff --git a/src/views/install/index.tpl b/src/views/install/index.tpl new file mode 100644 index 0000000000..75d6c1e16a --- /dev/null +++ b/src/views/install/index.tpl @@ -0,0 +1,146 @@ + + + + + + + NodeBB Web Installer + + + + + + + + + + + + + +
    +

    +

    Welcome to the NodeBB Installer

    + You are just a few steps away from launching your own NodeBB forum! +

    +
    +
    +

    +

    Create an Administrator account

    +
    +

    + +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + + + + + +
    +

    +

    Configure your database

    +
    +

    + +
    +
    + + +
    +
    There was an error connecting to your database. Please try again.
    +
    + +
    +
    + + + +
    +
    + + +
    +

    +

    Congratulations! Your NodeBB has been set-up.

    + + +

    +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/src/webserver.js b/src/webserver.js index bd4acb231f..7e8e01ed92 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -5,7 +5,7 @@ var path = require('path'), fs = require('fs'), nconf = require('nconf'), express = require('express'), - WebServer = express(), + app = express(), server, winston = require('winston'), async = require('async'), @@ -18,163 +18,216 @@ var path = require('path'), routes = require('./routes'), emitter = require('./emitter'), - helpers = require('./../public/src/modules/helpers'), - net; + helpers = require('../public/src/modules/helpers'); -if(nconf.get('ssl')) { +if (nconf.get('ssl')) { server = require('https').createServer({ key: fs.readFileSync(nconf.get('ssl').key), cert: fs.readFileSync(nconf.get('ssl').cert) - }, WebServer); + }, app); } else { - server = require('http').createServer(WebServer); + server = require('http').createServer(app); } -(function (app) { - var port = nconf.get('port'); +module.exports.server = server; - module.exports.init = function() { - var skipJS, skipLess, fromFile = nconf.get('from-file') || ''; +server.on('error', function(err) { + winston.error(err); + if (err.code === 'EADDRINUSE') { + winston.error('NodeBB address in use, exiting...'); + process.exit(0); + } else { + throw err; + } +}); - emailer.registerApp(app); +if (server.setTimeout) { + server.setTimeout(10000); +} - if (fromFile.match('js')) { - winston.info('[minifier] Minifying client-side JS skipped'); - skipJS = true; - } +module.exports.listen = function() { + emailer.registerApp(app); - if (fromFile.match('less')) { - winston.info('[minifier] Compiling LESS files skipped'); - skipLess = true; - } + middleware = middleware(app); + + helpers.register(); + + logger.init(app); + + emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() { + winston.info('NodeBB Ready'); + emitter.emit('nodebb:ready'); + listen(); + }); - // Preparation dependent on plugins - plugins.ready(function() { + initializeNodeBB(function(err) { + if (err) { + winston.error(err); + process.exit(); + } + if (process.send) { + process.send({ + action: 'ready' + }); + } + }); +}; + +function initializeNodeBB(callback) { + var skipJS, skipLess, fromFile = nconf.get('from-file') || ''; + + if (fromFile.match('js')) { + winston.info('[minifier] Minifying client-side JS skipped'); + skipJS = true; + } + + if (fromFile.match('less')) { + winston.info('[minifier] Compiling LESS files skipped'); + skipLess = true; + } + + async.waterfall([ + async.apply(cacheStaticFiles), + async.apply(meta.themes.setupPaths), + function(next) { + plugins.init(app, middleware, next); + }, + function(next) { async.parallel([ + async.apply(meta.templates.compile), async.apply(!skipJS ? meta.js.minify : meta.js.getFromFile, app.enabled('minification')), async.apply(!skipLess ? meta.css.minify : meta.css.getFromFile), async.apply(meta.sounds.init) - ]); - }); - - middleware = middleware(app); - routes(app, middleware); + ], next); + }, + function(results, next) { + plugins.fireHook('static:app.preload', { + app: app, + middleware: middleware + }, next); + }, + function(next) { + routes(app, middleware); + next(); + } + ], callback); +} - // Load server-side template helpers - helpers.register(); +function cacheStaticFiles(callback) { + if (global.env === 'development') { + return callback(); + } - // Cache static files on production - if (global.env !== 'development') { - app.enable('cache'); - app.enable('minification'); + app.enable('cache'); + app.enable('minification'); - // Configure cache-buster timestamp - require('child_process').exec('git describe --tags', { - cwd: path.join(__dirname, '../') - }, function(err, stdOut) { - if (!err) { - meta.config['cache-buster'] = stdOut.trim(); - } else { - fs.stat(path.join(__dirname, '../package.json'), function(err, stats) { - meta.config['cache-buster'] = new Date(stats.mtime).getTime(); - }); + // Configure cache-buster timestamp + require('child_process').exec('git describe --tags', { + cwd: path.join(__dirname, '../') + }, function(err, stdOut) { + if (!err) { + meta.config['cache-buster'] = stdOut.trim(); + callback(); + } else { + fs.stat(path.join(__dirname, '../package.json'), function(err, stats) { + if (err) { + return callback(err); } + meta.config['cache-buster'] = new Date(stats.mtime).getTime(); + callback(); }); } + }); +} - if (port !== 80 && port !== 443 && nconf.get('use_port') === false) { - winston.info('Enabling \'trust proxy\''); - app.enable('trust proxy'); +function listen(callback) { + var port = nconf.get('port'); + + if (Array.isArray(port)) { + if (!port.length) { + winston.error('[startup] empty ports array in config.json'); + process.exit(); } - if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') { - winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md'); + winston.warn('[startup] If you want to start nodebb on multiple ports please use loader.js'); + winston.warn('[startup] Defaulting to first port in array, ' + port[0]); + port = port[0]; + if (!port) { + winston.error('[startup] Invalid port, exiting'); + process.exit(); } - }; - - server.on('error', function(err) { - winston.error(err.stack); - console.log(err.stack); - if (err.code === 'EADDRINUSE') { - winston.error('NodeBB address in use, exiting...'); - process.exit(0); - } else { - throw err; + } + + if (port !== 80 && port !== 443 && nconf.get('use_port') === false) { + winston.info('Enabling \'trust proxy\''); + app.enable('trust proxy'); + } + + if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') { + winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md'); + } + + var isSocket = isNaN(port), + args = isSocket ? [port] : [port, nconf.get('bind_address')], + bind_address = ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address')) + ':' + port, + oldUmask; + + args.push(function(err) { + if (err) { + winston.info('[startup] NodeBB was unable to listen on: ' + bind_address); + process.exit(); } - }); - module.exports.server = server; - - emitter.all(['templates:compiled', 'meta:js.compiled', 'meta:css.compiled'], function() { - winston.info('NodeBB Ready'); - emitter.emit('nodebb:ready'); + winston.info('NodeBB is now listening on: ' + (isSocket ? port : bind_address)); + if (oldUmask) { + process.umask(oldUmask); + } }); - server.setTimout && server.setTimeout(10000); - - module.exports.listen = function(callback) { - logger.init(app); - - var isSocket = isNaN(port), - args = isSocket ? [port] : [port, nconf.get('bind_address')], - bind_address = ((nconf.get('bind_address') === "0.0.0.0" || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address')) + ':' + port, - oldUmask; - - args.push(function(err) { - if (err) { - winston.info('[startup] NodeBB was unable to listen on: ' + bind_address); - return callback(err); + // Alter umask if necessary + if (isSocket) { + oldUmask = process.umask('0000'); + module.exports.testSocket(port, function(err) { + if (!err) { + server.listen.apply(server, args); + } else { + winston.error('[startup] NodeBB was unable to secure domain socket access (' + port + ')'); + winston.error('[startup] ' + err.message); + process.exit(); } - - winston.info('NodeBB is now listening on: ' + (isSocket ? port : bind_address)); - if (oldUmask) { - process.umask(oldUmask); - } - - callback(); }); + } else { + server.listen.apply(server, args); + } +} - // Alter umask if necessary - if (isSocket) { - oldUmask = process.umask('0000'); - net = require('net'); - module.exports.testSocket(port, function(err) { - if (!err) { - server.listen.apply(server, args); +module.exports.testSocket = function(socketPath, callback) { + if (typeof socketPath !== 'string') { + return callback(new Error('invalid socket path : ' + socketPath)); + } + var net = require('net'); + async.series([ + function(next) { + fs.exists(socketPath, function(exists) { + if (exists) { + next(); } else { - winston.error('[startup] NodeBB was unable to secure domain socket access (' + port + ')'); - winston.error('[startup] ' + err.message); - process.exit(); + callback(); } }); - } else { - server.listen.apply(server, args); - } - }; - - module.exports.testSocket = function(socketPath, callback) { - async.series([ - function(next) { - fs.exists(socketPath, function(exists) { - if (exists) { - next(); - } else { - callback(); - } - }); - }, - function(next) { - var testSocket = new net.Socket(); - testSocket.on('error', function(err) { - next(err.code !== 'ECONNREFUSED' ? err : null); - }); - testSocket.connect({ path: socketPath }, function() { - // Something's listening here, abort - callback(new Error('port-in-use')); - }); - }, - async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way - ], callback); - }; - -}(WebServer)); + }, + function(next) { + var testSocket = new net.Socket(); + testSocket.on('error', function(err) { + next(err.code !== 'ECONNREFUSED' ? err : null); + }); + testSocket.connect({ path: socketPath }, function() { + // Something's listening here, abort + callback(new Error('port-in-use')); + }); + }, + async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way + ], callback); +}; + + diff --git a/tests/database/sorted.js b/tests/database/sorted.js index 6680ee8716..5c34c380b8 100644 --- a/tests/database/sorted.js +++ b/tests/database/sorted.js @@ -347,6 +347,10 @@ describe('Sorted Set methods', function() { }); describe('isSortedSetMember()', function() { + before(function(done) { + db.sortedSetAdd('zeroscore', 0, 'itemwithzeroscore', done); + }); + it('should return false if sorted set does not exist', function(done) { db.isSortedSetMember('doesnotexist', 'value1', function(err, isMember) { assert.equal(err, null); @@ -373,6 +377,14 @@ describe('Sorted Set methods', function() { done(); }); }); + + it('should return true if element is in sorted set with score 0', function(done) { + db.isSortedSetMember('zeroscore', 'itemwithzeroscore', function(err, isMember) { + assert.ifError(err); + assert.deepEqual(isMember, true); + done(); + }); + }); }); describe('isSortedSetMembers()', function() { diff --git a/tests/mocks/databasemock.js b/tests/mocks/databasemock.js index d3dea94a8c..a895d25c8a 100644 --- a/tests/mocks/databasemock.js +++ b/tests/mocks/databasemock.js @@ -45,6 +45,14 @@ ' "password": "",' + '\n' + ' "database": "1"' + '\n' + '}\n'+ + ' or (mongo) in a replicaset' + '\n' + + '"test_database": {' + '\n' + + ' "host": "127.0.0.1,127.0.0.1,127.0.0.1",' + '\n' + + ' "port": "27017,27018,27019",' + '\n' + + ' "username": "",' + '\n' + + ' "password": "",' + '\n' + + ' "database": "nodebb_test"' + '\n' + + '}\n'+ '===========================================================' ); winston.error(errorText); diff --git a/tests/user.js b/tests/user.js index 5c2f5bfd62..9a1ee4b90f 100644 --- a/tests/user.js +++ b/tests/user.js @@ -195,7 +195,8 @@ describe('User', function() { }); describe('passwordReset', function() { - var uid; + var uid, + code; before(function(done) { User.create({username: 'resetuser', password: '123456', email: 'reset@me.com'}, function(err, newUid) { assert.ifError(err); @@ -204,21 +205,49 @@ describe('User', function() { }); }); - it('should create a new reset code and reset password', function(done) { + it('.generate() should generate a new reset code', function(done) { + User.reset.generate(uid, function(err, _code) { + assert.ifError(err); + assert(_code); + + code = _code; + done(); + }); + }); + + it('.validate() should ensure that this new code is valid', function(done) { + User.reset.validate(code, function(err, valid) { + assert.ifError(err); + assert.strictEqual(valid, true); + done(); + }); + }); + + it('.validate() should correctly identify an invalid code', function(done) { + User.reset.validate(code + 'abcdef', function(err, valid) { + assert.ifError(err); + assert.strictEqual(valid, false); + done(); + }); + }); + + it('.send() should create a new reset code and reset password', function(done) { User.reset.send('reset@me.com', function(err, code) { assert.ifError(err); - assert(code); + done(); + }); + }); - User.reset.commit(code, 'newpassword', function(err) { - assert.ifError(err); + it('.commit() should update the user\'s password', function(done) { + User.reset.commit(code, 'newpassword', function(err) { + assert.ifError(err); - db.getObjectField('user:' + uid, 'password', function(err, newPassword) { + db.getObjectField('user:' + uid, 'password', function(err, newPassword) { + assert.ifError(err); + Password.compare('newpassword', newPassword, function(err, match) { assert.ifError(err); - Password.compare('newpassword', newPassword, function(err, match) { - assert.ifError(err); - assert(match); - done(); - }); + assert(match); + done(); }); }); });