From 61f37062cbe8592f973d03a8f768755d68a091fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 15 Jun 2017 10:56:30 -0500 Subject: [PATCH 01/26] Update Dockerfile to be based on the standard NodeJS image --- Dockerfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index c2cb68fe..0f759e57 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,8 @@ -FROM centos -RUN curl --silent --location https://rpm.nodesource.com/setup_7.x | bash - -RUN yum install -y git make gcc nodejs ImageMagick && yum clean all +FROM node:8.1 + COPY . /app WORKDIR /app/ -ENV NODE_ENV production +ENV NODE_ENV docker RUN npm install --no-progress --production && npm install --no-progress passport-ldapjs EXPOSE 3000 -CMD ["/usr/bin/node", "index.js"] \ No newline at end of file +CMD ["node", "index.js"] \ No newline at end of file From 82a1aec72a8c356f23089eecea1c87f54dbd0dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 15 Jun 2017 11:20:56 -0500 Subject: [PATCH 02/26] Add docker.toml with configs compatible with docker-compose --- config/docker.toml | 186 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 config/docker.toml diff --git a/config/docker.toml b/config/docker.toml new file mode 100644 index 00000000..38a76107 --- /dev/null +++ b/config/docker.toml @@ -0,0 +1,186 @@ +# This file is the default config file for Mailtrain. To use a environment specific +# configuration add new file {ENV}.{ext} (eg. "production.toml") to the same folder. +# {ENV} is defined by NODE_ENV environment variable. +# +# Do not modify this file directly, otherwise you might lose your modifications when upgrading +# +# You should only define the options you want to change in your additional config file. +# For example if the only thing you want to change is the port number for the www server +# then your additional config file should look like this: +# # production.toml +# [www] +# port=80 +# or if you want to use Javascript instead of TOML then the same file could look like this: +# // production.js +# module.exports = { +# www: { +# port: 80 +# } +# }; + +# Process title visible in monitoring logs and process listing +title="mailtrain" + +# Enabled HTML editors +editors=[ + ["summernote", "Summernote"], + ["grapejs", "GrapeJS"], + ["mosaico", "Mosaico"], + ["codeeditor", "Code Editor"] +] + +# Default language to use +language="en" + +# Inject custom styles in layout.hbs +# customstyles=["/custom/hello-world.css"] + +# Inject custom scripts in layout.hbs +# customscripts=["/custom/hello-world.js"] + +# Inject custom scripts in subscription/layout.mjml.hbs +# customsubscriptionscripts=["/custom/hello-world.js"] + +# If you start out as a root user (eg. if you want to use ports lower than 1000) +# then you can downgrade the user once all services are up and running +#user="mailtrain" +#group="mailtrain" + +# If Mailtrain is started as root, "Reports" feature drops the privileges of script generating the report to disallow +# any modifications of Mailtrain code and even prohibits reading the production configuration (which contains the MySQL +# password for read/write operations). The rouser/rogroup determines the user to be used +#rouser="nobody" +#rogroup="nogroup" + +[log] +# silly|verbose|info|http|warn|error|silent +level="verbose" + +[www] +# HTTP port to listen on +port=3000 +# HTTP interface to listen on +host="0.0.0.0" +# Secret for signing the session ID cookie +secret="a cat" +# Session length in seconds when "remember me" is checked +remember=2592000 # 30 days +# logger interface for expressjs morgan +log="dev" +# Is the server behind a proxy? true/false +# Set this to true if you are serving Mailtrain as a virtual domain through Nginx or Apache +proxy=false +# maximum POST body size +postsize="2MB" +# Uncomment to set uploads folder location for temporary data. Defaults to os.tmpdir() +# If the service is started by `npm start` then os.tmpdir() points to CWD +#tmpdir="/tmp" + +[mysql] +host="mysql" +user="mailtrain" +password="mailtrain" +database="mailtrain" +# Some installations, eg. MAMP can use a different port (8889) +# MAMP users should also turn on "Allow network access to MySQL" otherwise MySQL might not be accessible +port=3306 +charset="utf8mb4" +timezone="local" + +[redis] +# enable to use Redis session cache or disable if Redis is not installed +enabled=true +host="redis" +port=6379 +db=5 +# Uncomment if your Redis installation requires a password +#password="" + +[verp] +# Enable to start an MX server that detects bounced messages using VERP +# In most cases you do not want to use it +# Requires root privileges +enabled=false +port=2525 +host="0.0.0.0" +# With DMARC, the Return-Path and From address must match the same domain. +# By default we get around this by using the VERP address in the Sender header, +# with the side effect that some email clients diplay an ugly "on behalf of" message. +# You can safely disable this Sender header if you're not using DMARC or your +# VERP hostname is in the same domain as the From address. +# disablesenderheader=true + +[ldap] +# enable to use ldap user backend +enabled=false +host="localhost" +port=3002 +baseDN="ou=users,dc=company" +filter="(|(username={{username}})(mail={{username}}))" +#Username field in LDAP (uid/cn/username) +uidTag="username" +passwordresetlink="" + +[postfixbounce] +# Enable to allow writing Postfix bounce log to Mailtrain listener +# If enabled, tail mail.log to Mailtrain with the following command: +# tail -f -n +0 /var/log/mail.log | nc localhost 5699 - +enabled=false +port=5699 +# allow connections from localhost only +host="127.0.0.1" + +# extra options for nodemailer +[nodemailer] +#textEncoding="base64" + +[queue] +# How many parallel sender processes to spawn +# You can use more than 1 process only if you have Redis enabled +processes=1 + +[cors] +# Allow subscription widgets to be embedded +# origins=['https://www.example.com'] + +[mosaico] +# Installed templates +templates=[["versafix-1", "Versafix One"]] +# Inject custom scripts +# customscripts=["/mosaico/custom/my-mosaico-plugin.js"] + +[grapejs] +# Installed templates +templates=[ + ["demo", "HTML Template"], + ["aves", "MJML Template"] +] + +[reports] +# The whole reporting functionality can be disabled below if the they are not needed and the DB cannot be +# properly protected. +# Reports rely on custom user defined Javascript snippets defined in the report template. The snippets are run on the +# server when generating a report. As these snippets are stored in the DB, they pose a security risk because they can +# help gaining access to the server if the DB cannot +# be properly protected (e.g. if it is shared with another application with security weaknesses). +# Mailtrain mitigates this problem by running the custom Javascript snippets in a chrooted environment and under a +# DB user that cannot modify the database (see userRO in [mysql] above). However the chrooted environment is available +# only if Mailtrain is started as root. The chrooted environment still does not prevent the custom JS script in +# performing network operations and in generating XSS attacks as part of the report. +# The bottom line is that if people who are creating report templates or have write access to the DB cannot be trusted, +# then it's safer to switch off the reporting functionality below. +enabled=false + +[testserver] +# Starts a vanity server that redirects all mail to /dev/null +# Mostly needed for local development +enabled=false +port=5587 +mailboxserverport=3001 +host="0.0.0.0" +username="testuser" +password="testpass" +logger=false + +[seleniumwebdriver] +browser="phantomjs" From ca096d191fd6982d52dd50bdfbe9bd7a3b93b7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 15 Jun 2017 11:21:22 -0500 Subject: [PATCH 03/26] Add base docker-compose and override to build image and set ports --- docker-compose.override.yml | 8 ++++++++ docker-compose.yml | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 docker-compose.override.yml create mode 100644 docker-compose.yml diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 00000000..19ac21da --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,8 @@ +version: '2' +services: + mailtrain: + build: ./ + # volumes: + # - ./:/app + ports: + - "3000:3000" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..2c57a566 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: '2' +services: + mysql: + image: mysql:5.7 + environment: + - MYSQL_ROOT_PASSWORD=mailtrain + - MYSQL_DATABASE=mailtrain + - MYSQL_USER=mailtrain + - MYSQL_PASSWORD=mailtrain + volumes: + - mailtrain-mysq-data:/var/lib/mysql + redis: + image: redis:3.0 + volumes: + - mailtrain-redis-data:/data + mailtrain: + image: mailtrain:1.24 + depends_on: + - mysql + - redis + volumes: + - mailtrain-node-config:/app/config + - mailtrain-node-data:/app/public/grapejs/uploads + - mailtrain-node-data:/app/public/mosaico/uploads +volumes: + mailtrain-mysq-data: {} + mailtrain-redis-data: {} + mailtrain-node-data: {} + mailtrain-node-config: {} From 2cf2cc71ffef65594e82513ca0c5699a07f62a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 15 Jun 2017 12:59:42 -0500 Subject: [PATCH 04/26] Update mailtrain tag name --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2c57a566..fe82724f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: volumes: - mailtrain-redis-data:/data mailtrain: - image: mailtrain:1.24 + image: mailtrain:latest depends_on: - mysql - redis From 274c706f02f592428aeb6c13109cf46b41560e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 15 Jun 2017 13:00:00 -0500 Subject: [PATCH 05/26] Add restart: always to docker-compose.override.yml --- docker-compose.override.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 19ac21da..24b48ec6 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,8 +1,14 @@ version: '2' services: + mysql: + restart: always + redis: + restart: always mailtrain: build: ./ # volumes: # - ./:/app ports: - - "3000:3000" \ No newline at end of file + - "3000:3000" + restart: always + \ No newline at end of file From 5e5e5c70bf227a9559acecfa5d3f179b8c5c811b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 15 Jun 2017 13:00:17 -0500 Subject: [PATCH 06/26] Update README to include new docker-compose instructions --- README.md | 80 ++++++++----------------------------------------------- 1 file changed, 11 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 87fd2aea..7018101c 100644 --- a/README.md +++ b/README.md @@ -121,76 +121,18 @@ With proper SPF, DKIM and PTR records (DMARC wouldn't hurt either) I got perfect ### Simple Install (Docker) ##### Requirements: - * Docker - * docker-compose + * [Docker](https://www.docker.com/) + * [Docker Compose](https://docs.docker.com/compose/) - 1. Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain` - 2. Run `sudo docker build -t mailtrain-node:latest .` - 3. Copy default.toml to production.toml. Run `sudo mkdir -p /etc/mailtrain && sudo cp config/default.toml /etc/mailtrain/production.toml` - 4. Create `/etc/docker-compose.yml`. Example (dont forget change MYSQL_ROOT_PASS and MYSQL_USER_PASSWORD to your passwords): - ``` - version: '2' - services: - mailtrain-mysql: - image: mysql:latest - ports: - - "3306:3306" - container_name: "mailtrain-mysql" - restart: always - environment: - MYSQL_ROOT_PASSWORD: "MYSQL_ROOT_PASS" - MYSQL_DATABASE: "mailtrain" - MYSQL_USER: "mailtrain" - MYSQL_PASSWORD: "MYSQL_USER_PASSWORD" - volumes: - - mailtrain-mysq-data:/var/lib/mysql - - mailtrain-redis: - image: redis:3.0 - container_name: "mailtrain-redis" - volumes: - - mailtrain-redis-data:/data - - mailtrain-node: - image: mailtrain-node:latest - container_name: "mailtrain-node" - links: - - "mailtrain-mysql:mailtrain-mysql" - - "mailtrain-redis:mailtrain-redis" - ports: - - "3000:3000" - volumes: - - "/etc/mailtrain/production.toml:/app/config/production.toml" - - "mailtrain-node-data:/app/public/grapejs/uploads" - - "mailtrain-node-data:/app/public/mosaico/uploads" - volumes: - mailtrain-mysq-data: {} - mailtrain-redis-data: {} - mailtrain-node-data: {} - - ``` - 5. Update MySQL and Redis credintial in `/etc/mailtrain/production.toml` like this: - ``` - [mysql] - host="mailtrain-mysql" - user="mailtrain" - password="MYSQL_USER_PASSWORD" - database="mailtrain" - port=3306 - charset="utf8mb4" - timezone="UTC" - - [redis] - enabled=true - host="mailtrain-redis" - port=6379 - db=5 - ``` - 6. Run docker container with command `sudo docker-compose -f /etc/docker-compose.yml up -d` - 7. Open [http://localhost:3000/](http://localhost:3000/) - 8. Authenticate as `admin`:`test` - 9. Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration - 10. Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password + * Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain` + * **Note**: depending on how you have configured your system and Docker you may need to prepend the commands below with `sudo`. + * Bring up the stack with: `docker-compose up -d`, by default it will use the included `docker-compose.yml` file and override some configurations taken from the `docker-compose.override.yml` file. + * You might want to modify the `docker-compose.yml` or `docker-compose.override.yml` file, modify port mappings, change volume paths, etc. + * If you want to use only / copy the `docker-compose.yml` file (for example, if you were deploying with Rancher), you may need to first run `docker-compose build` to make sure your system has a Docker image `mailtrain:latest`. + * Open [http://localhost:3000/](http://localhost:3000/) (change the host name `localhost` to the name of the host where you are deploying the system). + * Authenticate as `admin`:`test` + * Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration. + * Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password. ### Manual Install (any OS that supports Node.js) From 277b2cadf5ce626f57395a64febb18350f6c9e46 Mon Sep 17 00:00:00 2001 From: witzig Date: Wed, 21 Jun 2017 17:28:56 +0200 Subject: [PATCH 07/26] Fixed bug #261 --- routes/editorapi.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routes/editorapi.js b/routes/editorapi.js index a285fab6..58cbdf30 100644 --- a/routes/editorapi.js +++ b/routes/editorapi.js @@ -375,6 +375,7 @@ router.post('/upload', passport.csrfProtection, (req, res) => { }; const dirName = getDirName(); + const serviceUrlParts = url.parse(serviceUrl); if (dirName === false) { return res.status(500).send(_('Invalid resource type or ID')); @@ -391,7 +392,8 @@ router.post('/upload', passport.csrfProtection, (req, res) => { uploadDir: path.join(__dirname, '..', 'public', req.query.editor, 'uploads', dirName), uploadUrl: '/' + req.query.editor + '/uploads/' + dirName, // must be root relative acceptFileTypes: /\.(gif|jpe?g|png)$/i, - hostname: url.parse(serviceUrl).host // include port + hostname: serviceUrlParts.host, // include port + ssl: serviceUrlParts.protocol === 'https:' }; const mockres = httpMocks.createResponse({ From 963aef53b50727a9db146667c16a688178aa332c Mon Sep 17 00:00:00 2001 From: witzig Date: Thu, 22 Jun 2017 18:00:13 +0200 Subject: [PATCH 08/26] Don't close the VERP Server on error. And improved error reporting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on https://git.io/vQLS7 , I believe the change introduced in https://git.io/vQL7u "server.close()" to be in error, and intended to close one connection, like it’s handled in https://git.io/vQLSF Perhaps SMTPServer isn't even meant to throw ECONNRESET and EPIPE in the first place. --- services/postfix-bounce-server.js | 42 +++++++++++++++++++++++-------- services/test-server.js | 4 +++ services/verp-server.js | 32 +++++++++++++++++++---- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index c4c96e65..bc8bcede 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -70,17 +70,37 @@ let server = net.createServer(socket => { }); }); -server.on('error', err => { - log.error('POSTFIXBOUNCE', err && err.stack); -}); - module.exports = callback => { - if (config.postfixbounce.enabled) { - server.listen(config.postfixbounce.port, config.postfixbounce.host, () => { - log.info('POSTFIXBOUNCE', 'Server listening on port %s', config.postfixbounce.port); - setImmediate(callback); - }); - } else { - setImmediate(callback); + if (!config.postfixbounce.enabled) { + return setImmediate(callback); } + + let started = false; + + server.on('error', err => { + const port = config.postfixbounce.port; + const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; + + switch (err.code) { + case 'EACCES': + log.error('POSTFIXBOUNCE', '%s requires elevated privileges.', bind); + break; + case 'EADDRINUSE': + log.error('POSTFIXBOUNCE', '%s is already in use', bind); + break; + default: + log.error('POSTFIXBOUNCE', err); + } + + if (!started) { + started = true; + return callback(err); + } + }); + + server.listen(config.postfixbounce.port, config.postfixbounce.host, () => { + started = true; + log.info('POSTFIXBOUNCE', 'Server listening on port %s', config.postfixbounce.port); + setImmediate(callback); + }); }; diff --git a/services/test-server.js b/services/test-server.js index 1f4777b8..ed549c67 100644 --- a/services/test-server.js +++ b/services/test-server.js @@ -164,6 +164,10 @@ let mailBoxServer = http.createServer((req, res) => { }); }); +mailBoxServer.on('error', err => { + log.error('Test SMTP Mailbox Server', err); +}); + module.exports = callback => { if (config.testserver.enabled) { server.listen(config.testserver.port, config.testserver.host, () => { diff --git a/services/verp-server.js b/services/verp-server.js index 95964daf..37bd17d1 100644 --- a/services/verp-server.js +++ b/services/verp-server.js @@ -99,15 +99,36 @@ let server = new SMTPServer({ } }); -server.on('error', err => { - log.error('VERP', err); - server.close(); -}); - module.exports = callback => { if (!config.verp.enabled) { return setImmediate(callback); } + + let started = false; + + server.on('error', err => { + const port = config.verp.port; + const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; + + switch (err.code) { + case 'EACCES': + log.error('VERP', '%s requires elevated privileges', bind); + break; + case 'EADDRINUSE': + log.error('VERP', '%s is already in use', bind); + break; + case 'ECONNRESET': // Usually happens when a client does not disconnect cleanly + case 'EPIPE': // Remote connection was closed before the server attempted to send data + default: + log.error('VERP', err); + } + + if (!started) { + started = true; + return callback(err); + } + }); + let hosts; if (typeof config.verp.host === 'string' && config.verp.host) { hosts = config.verp.host.trim().split(',').map(host => host.trim()).filter(host => host.trim()); @@ -121,6 +142,7 @@ module.exports = callback => { let pos = 0; let startNextHost = () => { if (pos >= hosts.length) { + started = true; return setImmediate(callback); } let host = hosts[pos++]; From f73eb026b69a70e1444726d6b92cb2bbb5755a6e Mon Sep 17 00:00:00 2001 From: witzig Date: Thu, 22 Jun 2017 18:16:26 +0200 Subject: [PATCH 09/26] Fixed possible bug: callback called twice --- services/postfix-bounce-server.js | 3 +++ services/verp-server.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index bc8bcede..60aba027 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -99,6 +99,9 @@ module.exports = callback => { }); server.listen(config.postfixbounce.port, config.postfixbounce.host, () => { + if (started) { + return server.close(); + } started = true; log.info('POSTFIXBOUNCE', 'Server listening on port %s', config.postfixbounce.port); setImmediate(callback); diff --git a/services/verp-server.js b/services/verp-server.js index 37bd17d1..d1fac80d 100644 --- a/services/verp-server.js +++ b/services/verp-server.js @@ -147,6 +147,9 @@ module.exports = callback => { } let host = hosts[pos++]; server.listen(config.verp.port, host, () => { + if (started) { + return server.close(); + } log.info('VERP', 'Server listening on %s:%s', host || '*', config.verp.port); setImmediate(startNextHost); }); From 44ab04624b650052c619d4a8716a25dbdf10102a Mon Sep 17 00:00:00 2001 From: witzig Date: Thu, 22 Jun 2017 18:20:55 +0200 Subject: [PATCH 10/26] Satisfy eslint rule linebreak-style --- Gruntfile.js | 2 +- services/executor.js | 262 +++++++++++++++++++++---------------------- 2 files changed, 132 insertions(+), 132 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 1ec322bc..797686e6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -5,7 +5,7 @@ module.exports = function (grunt) { // Project configuration. grunt.initConfig({ eslint: { - all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'Gruntfile.js', 'app.js', 'index.js', 'routes/editorapi.js'] + all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'services/**/*.js', 'Gruntfile.js', 'app.js', 'index.js', 'routes/editorapi.js'] }, nodeunit: { diff --git a/services/executor.js b/services/executor.js index 99a01028..4d10814e 100644 --- a/services/executor.js +++ b/services/executor.js @@ -1,131 +1,131 @@ -'use strict'; - -/* Privileged executor. If Mailtrain is started as root, this process keeps the root privilege to be able to spawn workers - that can chroot. - */ - -const fileHelpers = require('../lib/file-helpers'); -const fork = require('child_process').fork; -const path = require('path'); -const log = require('npmlog'); -const fs = require('fs'); -const privilegeHelpers = require('../lib/privilege-helpers'); - -let processes = {}; - -function spawnProcess(tid, executable, args, outFile, errFile, cwd, uid, gid) { - - function reportFail(msg) { - process.send({ - type: 'process-failed', - msg, - tid - }); - } - - fs.open(outFile, 'w', (err, outFd) => { - if (err) { - log.error('Executor', err); - reportFail('Cannot create standard output file.'); - return; - } - - fs.open(errFile, 'w', (err, errFd) => { - if (err) { - log.error('Executor', err); - reportFail('Cannot create standard error file.'); - return; - } - - privilegeHelpers.ensureMailtrainOwner(outFile, (err) => { - if (err) { - log.warn('Executor', 'Cannot change owner of output file of process tid:%s.', tid) - } - - privilegeHelpers.ensureMailtrainOwner(errFile, (err) => { - if (err) { - log.warn('Executor', 'Cannot change owner of error output file of process tid:%s.', tid) - } - - const options = { - stdio: ['ignore', outFd, errFd, 'ipc'], - cwd, - env: {NODE_ENV: process.env.NODE_ENV}, - uid, - gid - }; - - let child; - - try { - child = fork(executable, args, options); - } catch (err) { - log.error('Executor', 'Cannot start process with tid:%s.', tid); - reportFail('Cannot start process.'); - return; - } - - const pid = child.pid; - processes[tid] = child; - - log.info('Executor', 'Process started with tid:%s pid:%s.', tid, pid); - process.send({ - type: 'process-started', - tid - }); - - child.on('close', (code, signal) => { - - delete processes[tid]; - log.info('Executor', 'Process tid:%s pid:%s exited with code %s signal %s.', tid, pid, code, signal); - - fs.close(outFd, (err) => { - if (err) { - log.error('Executor', err); - } - - fs.close(errFd, (err) => { - if (err) { - log.error('Executor', err); - } - - process.send({ - type: 'process-finished', - tid, - code, - signal - }); - }); - }); - }); - }); - }); - }); - }); -} - -process.on('message', msg => { - if (msg) { - const type = msg.type; - - if (type === 'start-report-processor-worker') { - - const ids = privilegeHelpers.getConfigROUidGid(); - spawnProcess(msg.tid, path.join(__dirname, '..', 'workers', 'reports', 'report-processor.js'), [msg.data.id], fileHelpers.getReportContentFile(msg.data), fileHelpers.getReportOutputFile(msg.data), path.join(__dirname, '..', 'workers', 'reports'), ids.uid, ids.gid); - - } else if (type === 'stop-process') { - const child = processes[msg.tid]; - - if (child) { - log.info('Executor', 'Killing process tid:%s pid:%s', msg.tid, child.pid); - child.kill(); - } else { - log.info('Executor', 'No running process found with tid:%s pid:%s', msg.tid, child.pid); - } - } - } -}); - -process.send({ - type: 'executor-started' -}); +'use strict'; + +/* Privileged executor. If Mailtrain is started as root, this process keeps the root privilege to be able to spawn workers + that can chroot. + */ + +const fileHelpers = require('../lib/file-helpers'); +const fork = require('child_process').fork; +const path = require('path'); +const log = require('npmlog'); +const fs = require('fs'); +const privilegeHelpers = require('../lib/privilege-helpers'); + +let processes = {}; + +function spawnProcess(tid, executable, args, outFile, errFile, cwd, uid, gid) { + + function reportFail(msg) { + process.send({ + type: 'process-failed', + msg, + tid + }); + } + + fs.open(outFile, 'w', (err, outFd) => { + if (err) { + log.error('Executor', err); + reportFail('Cannot create standard output file.'); + return; + } + + fs.open(errFile, 'w', (err, errFd) => { + if (err) { + log.error('Executor', err); + reportFail('Cannot create standard error file.'); + return; + } + + privilegeHelpers.ensureMailtrainOwner(outFile, (err) => { + if (err) { + log.warn('Executor', 'Cannot change owner of output file of process tid:%s.', tid) + } + + privilegeHelpers.ensureMailtrainOwner(errFile, (err) => { + if (err) { + log.warn('Executor', 'Cannot change owner of error output file of process tid:%s.', tid) + } + + const options = { + stdio: ['ignore', outFd, errFd, 'ipc'], + cwd, + env: {NODE_ENV: process.env.NODE_ENV}, + uid, + gid + }; + + let child; + + try { + child = fork(executable, args, options); + } catch (err) { + log.error('Executor', 'Cannot start process with tid:%s.', tid); + reportFail('Cannot start process.'); + return; + } + + const pid = child.pid; + processes[tid] = child; + + log.info('Executor', 'Process started with tid:%s pid:%s.', tid, pid); + process.send({ + type: 'process-started', + tid + }); + + child.on('close', (code, signal) => { + + delete processes[tid]; + log.info('Executor', 'Process tid:%s pid:%s exited with code %s signal %s.', tid, pid, code, signal); + + fs.close(outFd, (err) => { + if (err) { + log.error('Executor', err); + } + + fs.close(errFd, (err) => { + if (err) { + log.error('Executor', err); + } + + process.send({ + type: 'process-finished', + tid, + code, + signal + }); + }); + }); + }); + }); + }); + }); + }); +} + +process.on('message', msg => { + if (msg) { + const type = msg.type; + + if (type === 'start-report-processor-worker') { + + const ids = privilegeHelpers.getConfigROUidGid(); + spawnProcess(msg.tid, path.join(__dirname, '..', 'workers', 'reports', 'report-processor.js'), [msg.data.id], fileHelpers.getReportContentFile(msg.data), fileHelpers.getReportOutputFile(msg.data), path.join(__dirname, '..', 'workers', 'reports'), ids.uid, ids.gid); + + } else if (type === 'stop-process') { + const child = processes[msg.tid]; + + if (child) { + log.info('Executor', 'Killing process tid:%s pid:%s', msg.tid, child.pid); + child.kill(); + } else { + log.info('Executor', 'No running process found with tid:%s pid:%s', msg.tid, child.pid); + } + } + } +}); + +process.send({ + type: 'executor-started' +}); From c7ba0235c5c09456aa3323d95a09ce531d9968f0 Mon Sep 17 00:00:00 2001 From: witzig Date: Thu, 22 Jun 2017 18:24:53 +0200 Subject: [PATCH 11/26] Satisfy eslint rule indent --- services/importer.js | 112 +++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/services/importer.js b/services/importer.js index 02c70a87..143f6250 100644 --- a/services/importer.js +++ b/services/importer.js @@ -141,72 +141,72 @@ function processImport(data, callback) { } function insertToSubscription() { - subscriptions.insert(listId, { - imported: data.id, - status: data.type, - partial: true - }, entry, (err, response) => { - if (err) { - // ignore - log.error('Import', err.stack); - } else if (response.entryId) { - //log.verbose('Import', 'Inserted %s as %s', entry.email, entryId); - } + subscriptions.insert(listId, { + imported: data.id, + status: data.type, + partial: true + }, entry, (err, response) => { + if (err) { + // ignore + log.error('Import', err.stack); + } else if (response.entryId) { + //log.verbose('Import', 'Inserted %s as %s', entry.email, entryId); + } - db.getConnection((err, connection) => { - if (err) { - log.error('Import', err.stack); - return setImmediate(processRows); - } - - let query; - if (response.inserted) { - // this record did not exist before, count as new - query = 'UPDATE importer SET `processed`=`processed`+1, `new`=`new`+1 WHERE `id`=? LIMIT 1'; - } else { - // it's an existing record - query = 'UPDATE importer SET `processed`=`processed`+1 WHERE `id`=? LIMIT 1'; - } - - connection.query(query, [data.id], () => { - connection.release(); - return setImmediate(processRows); - }); - }); - }); - } - - if (data.emailcheck === 1) { - tools.validateEmail(entry.email, true, err => { - if (err) { - let reason = (err.message || '').toString().trim().replace(/^[a-z]Error:\s*/i, ''); - log.verbose('Import', 'Failed processing row %s: %s', entry.email, reason); db.getConnection((err, connection) => { if (err) { log.error('Import', err.stack); return setImmediate(processRows); } - let query = 'INSERT INTO import_failed (`import`, `email`, `reason`) VALUES(?,?,?)'; - connection.query(query, [data.id, entry.email, reason], err => { - if (err) { - connection.release(); - return setImmediate(processRows); - } - let query = 'UPDATE importer SET `failed`=`failed`+1 WHERE `id`=? LIMIT 1'; - connection.query(query, [data.id], () => { - connection.release(); - return setImmediate(processRows); - }); + let query; + if (response.inserted) { + // this record did not exist before, count as new + query = 'UPDATE importer SET `processed`=`processed`+1, `new`=`new`+1 WHERE `id`=? LIMIT 1'; + } else { + // it's an existing record + query = 'UPDATE importer SET `processed`=`processed`+1 WHERE `id`=? LIMIT 1'; + } + + connection.query(query, [data.id], () => { + connection.release(); + return setImmediate(processRows); }); }); - return; - } + }); + } + + if (data.emailcheck === 1) { + tools.validateEmail(entry.email, true, err => { + if (err) { + let reason = (err.message || '').toString().trim().replace(/^[a-z]Error:\s*/i, ''); + log.verbose('Import', 'Failed processing row %s: %s', entry.email, reason); + db.getConnection((err, connection) => { + if (err) { + log.error('Import', err.stack); + return setImmediate(processRows); + } + + let query = 'INSERT INTO import_failed (`import`, `email`, `reason`) VALUES(?,?,?)'; + connection.query(query, [data.id, entry.email, reason], err => { + if (err) { + connection.release(); + return setImmediate(processRows); + } + let query = 'UPDATE importer SET `failed`=`failed`+1 WHERE `id`=? LIMIT 1'; + connection.query(query, [data.id], () => { + connection.release(); + return setImmediate(processRows); + }); + }); + }); + return; + } + insertToSubscription(); + }); + } else { insertToSubscription(); - }); - } else { - insertToSubscription(); - } + } }; parser.on('readable', () => { From 8b74730d08788434b4a2d5d25f4a37e41e26eaee Mon Sep 17 00:00:00 2001 From: witzig Date: Thu, 22 Jun 2017 18:29:42 +0200 Subject: [PATCH 12/26] Fixed remaining eslint errors for services/**/*.js --- services/executor.js | 12 ++++++------ services/feedcheck.js | 2 +- services/verp-server.js | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/services/executor.js b/services/executor.js index 4d10814e..42b1294f 100644 --- a/services/executor.js +++ b/services/executor.js @@ -37,14 +37,14 @@ function spawnProcess(tid, executable, args, outFile, errFile, cwd, uid, gid) { return; } - privilegeHelpers.ensureMailtrainOwner(outFile, (err) => { + privilegeHelpers.ensureMailtrainOwner(outFile, err => { if (err) { - log.warn('Executor', 'Cannot change owner of output file of process tid:%s.', tid) + log.warn('Executor', 'Cannot change owner of output file of process tid:%s.', tid); } - privilegeHelpers.ensureMailtrainOwner(errFile, (err) => { + privilegeHelpers.ensureMailtrainOwner(errFile, err => { if (err) { - log.warn('Executor', 'Cannot change owner of error output file of process tid:%s.', tid) + log.warn('Executor', 'Cannot change owner of error output file of process tid:%s.', tid); } const options = { @@ -79,12 +79,12 @@ function spawnProcess(tid, executable, args, outFile, errFile, cwd, uid, gid) { delete processes[tid]; log.info('Executor', 'Process tid:%s pid:%s exited with code %s signal %s.', tid, pid, code, signal); - fs.close(outFd, (err) => { + fs.close(outFd, err => { if (err) { log.error('Executor', err); } - fs.close(errFd, (err) => { + fs.close(errFd, err => { if (err) { log.error('Executor', err); } diff --git a/services/feedcheck.js b/services/feedcheck.js index 5f43c479..b2871d38 100644 --- a/services/feedcheck.js +++ b/services/feedcheck.js @@ -135,7 +135,7 @@ function checkEntries(parent, entries, callback) { if (/\[RSS_ENTRY[\w]*\]/i.test(html)) { html = html.replace(/\[RSS_ENTRY\]/, entry.content); //for backward compatibility Object.keys(entry).forEach(key => { - html = html.replace('\[RSS_ENTRY_'+key.toUpperCase()+'\]', entry[key]) + html = html.replace('[RSS_ENTRY_' + key.toUpperCase() + ']', entry[key]); }); } else { html = entry.content + html; diff --git a/services/verp-server.js b/services/verp-server.js index d1fac80d..24150147 100644 --- a/services/verp-server.js +++ b/services/verp-server.js @@ -29,7 +29,7 @@ let server = new SMTPServer({ let user = address.address.split('@').shift(); let host = address.address.split('@').pop(); - if (host !== configItems.verpHostname || !/^[a-z0-9_\-]+\.[a-z0-9_\-]+\.[a-z0-9_\-]+$/i.test(user)) { + if (host !== configItems.verpHostname || !/^[a-z0-9_-]+\.[a-z0-9_-]+\.[a-z0-9_-]+$/i.test(user)) { err = new Error('Unknown user ' + address.address); err.responseCode = 510; return callback(err); From 60c446b9100c4f45e54e0845fb44cbe856173dd5 Mon Sep 17 00:00:00 2001 From: Francisco Lapuente Date: Thu, 15 Jun 2017 09:54:51 +0200 Subject: [PATCH 13/26] Added spanish es_ES translations --- languages/es_ES.mo | Bin 0 -> 89847 bytes languages/es_ES.po | 4668 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 4668 insertions(+) create mode 100644 languages/es_ES.mo create mode 100644 languages/es_ES.po diff --git a/languages/es_ES.mo b/languages/es_ES.mo new file mode 100644 index 0000000000000000000000000000000000000000..818a6306fbe75fa9aa467aecd626a642302babfa GIT binary patch literal 89847 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

u9H2RMVMLr}3~>1w5;fJR*00w( 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 literal 0 HcmV?d00001 diff --git a/languages/es_ES.po b/languages/es_ES.po new file mode 100644 index 00000000..998c3e56 --- /dev/null +++ b/languages/es_ES.po @@ -0,0 +1,4668 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-08 10:55+0200\n" +"PO-Revision-Date: 2017-06-14 15:40+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.0.2\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: views/archive/layout.hbs:1 views/layout.hbs:1 +msgid "Self hosted email newsletter app" +msgstr "Aplicación de boletín de correo electrónico alojado por usted mismo" + +#: views/blacklist.hbs:1 views/campaigns/blacklisted.hbs:1 +#: views/campaigns/bounced.hbs:1 views/campaigns/campaigns.hbs:1 +#: views/campaigns/clicked.hbs:1 views/campaigns/complained.hbs:1 +#: views/campaigns/create-rss.hbs:1 views/campaigns/create-triggered.hbs:1 +#: views/campaigns/create.hbs:1 views/campaigns/delivered.hbs:1 +#: views/campaigns/edit-rss.hbs:1 views/campaigns/edit-triggered.hbs:1 +#: views/campaigns/edit.hbs:1 views/campaigns/opened.hbs:1 +#: views/campaigns/unsubscribed.hbs:1 views/campaigns/upload-attachment.hbs:1 +#: views/campaigns/view.hbs:1 views/lists/create.hbs:1 views/lists/edit.hbs:1 +#: views/lists/fields/create.hbs:1 views/lists/fields/edit.hbs:1 +#: views/lists/fields/fields.hbs:1 views/lists/forms/create.hbs:1 +#: views/lists/forms/edit.hbs:1 views/lists/forms/forms.hbs:1 +#: views/lists/lists.hbs:1 views/lists/segments/create.hbs:1 +#: views/lists/segments/edit.hbs:1 views/lists/segments/rule-configure.hbs:1 +#: views/lists/segments/rule-create.hbs:1 views/lists/segments/rule-edit.hbs:1 +#: views/lists/segments/segments.hbs:1 views/lists/segments/view.hbs:1 +#: views/lists/subscription/add.hbs:1 views/lists/subscription/edit.hbs:1 +#: views/lists/subscription/import-failed.hbs:1 +#: views/lists/subscription/import-preview.hbs:1 +#: views/lists/subscription/import.hbs:1 views/lists/view.hbs:1 +#: views/report-templates/create.hbs:1 views/report-templates/edit.hbs:1 +#: views/report-templates/report-templates.hbs:1 +#: views/reports/create-select-template.hbs:1 views/reports/create.hbs:1 +#: views/reports/edit.hbs:1 views/reports/output.hbs:1 +#: views/reports/reports.hbs:1 views/reports/view.hbs:1 views/settings.hbs:1 +#: views/templates/create.hbs:1 views/templates/edit.hbs:1 +#: views/templates/templates.hbs:1 views/triggers/create-select.hbs:1 +#: views/triggers/create.hbs:1 views/triggers/edit.hbs:1 +#: views/triggers/triggered.hbs:1 views/triggers/triggers.hbs:1 +#: views/users/account.hbs:1 views/users/api.hbs:1 views/users/forgot.hbs:1 +#: views/users/login.hbs:1 views/users/reset.hbs:1 app.js:169 +msgid "Home" +msgstr "Inicio" + +#: views/blacklist.hbs:2 views/blacklist.hbs:3 views/layout.hbs:7 +#: views/lists/subscription/edit.hbs:15 +msgid "Blacklist" +msgstr "Lista negra" + +#: views/blacklist.hbs:4 views/users/api.hbs:55 +msgid "Add email to blacklist" +msgstr "Añadir correo a la lista negra" + +#: views/blacklist.hbs:5 +msgid "Add" +msgstr "Añadir" + +#: views/blacklist.hbs:6 +msgid "Email" +msgstr "Correo eléctronico" + +#: views/campaigns/blacklisted.hbs:2 views/campaigns/bounced.hbs:2 +#: views/campaigns/campaigns.hbs:2 views/campaigns/campaigns.hbs:7 +#: views/campaigns/clicked.hbs:2 views/campaigns/complained.hbs:2 +#: views/campaigns/create-rss.hbs:2 views/campaigns/create-triggered.hbs:2 +#: views/campaigns/create.hbs:2 views/campaigns/delivered.hbs:2 +#: views/campaigns/edit-rss.hbs:2 views/campaigns/edit-triggered.hbs:2 +#: views/campaigns/edit.hbs:2 views/campaigns/opened.hbs:2 +#: views/campaigns/unsubscribed.hbs:2 views/campaigns/upload-attachment.hbs:2 +#: views/campaigns/view.hbs:2 lib/tools.js:133 routes/campaigns.js:35 +msgid "Campaigns" +msgstr "Campañas" + +#: views/campaigns/blacklisted.hbs:3 views/campaigns/blacklisted.hbs:4 +msgid "Blacklisted info" +msgstr "Información de la lista negra" + +#: views/campaigns/blacklisted.hbs:5 views/campaigns/bounced.hbs:5 +#: views/campaigns/clicked.hbs:5 views/campaigns/complained.hbs:5 +#: views/campaigns/delivered.hbs:5 views/campaigns/edit-rss.hbs:5 +#: views/campaigns/edit-triggered.hbs:5 views/campaigns/edit.hbs:5 +#: views/campaigns/opened.hbs:5 views/campaigns/unsubscribed.hbs:5 +#: views/campaigns/upload-attachment.hbs:6 +msgid "View campaign" +msgstr "Ver campaña" + +#: views/campaigns/blacklisted.hbs:6 +msgid "Subscribers who blacklisted by global blacklist:" +msgstr "Suscriptores que han sido incluidos en la lista negra global:" + +#: views/campaigns/blacklisted.hbs:7 views/campaigns/bounced.hbs:7 +#: views/campaigns/clicked.hbs:15 views/campaigns/complained.hbs:7 +#: views/campaigns/delivered.hbs:7 views/campaigns/opened.hbs:9 +#: views/campaigns/unsubscribed.hbs:7 +#: views/lists/subscription/import-failed.hbs:8 views/lists/view.hbs:19 +#: views/triggers/triggered.hbs:6 +msgid "Address" +msgstr "Dirección" + +#: views/campaigns/blacklisted.hbs:8 views/campaigns/bounced.hbs:8 +#: views/campaigns/clicked.hbs:16 views/campaigns/complained.hbs:8 +#: views/campaigns/delivered.hbs:8 views/campaigns/opened.hbs:10 +#: views/campaigns/unsubscribed.hbs:8 views/lists/subscription/add.hbs:6 +#: views/lists/subscription/edit.hbs:7 +#: views/lists/subscription/import-preview.hbs:7 views/lists/view.hbs:20 +#: views/subscription/partials/subscription-custom-fields.hbs:3 +#: views/triggers/triggered.hbs:7 +msgid "First Name" +msgstr "Nombre" + +#: views/campaigns/blacklisted.hbs:9 views/campaigns/bounced.hbs:9 +#: views/campaigns/clicked.hbs:17 views/campaigns/complained.hbs:9 +#: views/campaigns/delivered.hbs:9 views/campaigns/opened.hbs:11 +#: views/campaigns/unsubscribed.hbs:9 views/lists/subscription/add.hbs:7 +#: views/lists/subscription/edit.hbs:8 +#: views/lists/subscription/import-preview.hbs:8 views/lists/view.hbs:21 +#: views/subscription/partials/subscription-custom-fields.hbs:4 +#: views/triggers/triggered.hbs:8 +msgid "Last Name" +msgstr "Apellidos" + +#: views/campaigns/blacklisted.hbs:10 +msgid "Reason" +msgstr "Motivo" + +#: views/campaigns/blacklisted.hbs:11 +msgid "Time" +msgstr "Fecha" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Información del bounce" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Suscriptores que fueron bounce y unsuscritos:" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "Respuesta del SMTP" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Fecha" + +#: views/campaigns/campaigns.hbs:3 views/campaigns/create-triggered.hbs:26 +#: views/campaigns/create.hbs:3 views/campaigns/create.hbs:4 +#: views/campaigns/create.hbs:28 +msgid "Create Campaign" +msgstr "Crear Campaña" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Campaña normal" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "Campaña de RSS" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "Campaña encadenada" + +#: views/campaigns/campaigns.hbs:8 views/campaigns/create-rss.hbs:6 +#: views/campaigns/create-triggered.hbs:5 views/campaigns/create.hbs:5 +#: views/campaigns/edit-rss.hbs:8 views/campaigns/edit-triggered.hbs:9 +#: views/campaigns/edit.hbs:10 views/campaigns/view.hbs:73 +#: views/lists/create.hbs:5 views/lists/edit.hbs:6 +#: views/lists/fields/fields.hbs:6 views/lists/forms/forms.hbs:6 +#: views/lists/lists.hbs:5 views/lists/segments/segments.hbs:6 +#: views/report-templates/partials/report-template-fields.hbs:1 +#: views/report-templates/report-templates.hbs:10 +#: views/reports/partials/report-fields.hbs:1 +#: views/reports/partials/report-fields.hbs:5 +#: views/reports/partials/report-fields.hbs:9 views/reports/reports.hbs:6 +#: views/templates/templates.hbs:5 views/triggers/triggers.hbs:5 +msgid "Name" +msgstr "Nombre" + +#: views/campaigns/campaigns.hbs:9 views/campaigns/create-rss.hbs:8 +#: views/campaigns/create-triggered.hbs:7 views/campaigns/create.hbs:7 +#: views/campaigns/edit-rss.hbs:10 views/campaigns/edit-triggered.hbs:11 +#: views/campaigns/edit.hbs:12 views/campaigns/view.hbs:74 +#: views/lists/create.hbs:7 views/lists/edit.hbs:10 +#: views/lists/forms/edit.hbs:9 views/lists/forms/forms.hbs:7 +#: views/lists/lists.hbs:8 views/mosaico/editor.hbs:3 +#: views/partials/merge-tag-reference.hbs:4 +#: views/report-templates/partials/report-template-fields.hbs:3 +#: views/report-templates/report-templates.hbs:11 +#: views/reports/partials/report-fields.hbs:3 +#: views/reports/partials/report-fields.hbs:6 views/reports/reports.hbs:8 +#: views/templates/create.hbs:9 views/templates/edit.hbs:8 +#: views/templates/templates.hbs:6 views/triggers/create.hbs:7 +#: views/triggers/edit.hbs:8 views/triggers/triggers.hbs:7 +msgid "Description" +msgstr "Descripción" + +#: views/campaigns/campaigns.hbs:10 views/campaigns/view.hbs:75 +#: views/lists/view.hbs:22 views/lists/view.hbs:30 +#: views/triggers/triggers.hbs:6 +msgid "Status" +msgstr "Estado" + +#: views/campaigns/campaigns.hbs:11 views/campaigns/view.hbs:76 +#: views/lists/view.hbs:23 views/lists/view.hbs:24 +#: views/report-templates/report-templates.hbs:12 +#: views/reports/partials/report-fields.hbs:7 views/reports/reports.hbs:9 +msgid "Created" +msgstr "Fecha de Creación" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Información del enlace" + +#: views/campaigns/clicked.hbs:6 views/campaigns/view.hbs:63 +msgid "URL" +msgstr "URL" + +#: views/campaigns/clicked.hbs:7 views/campaigns/view.hbs:64 +msgid "Clicks" +msgstr "Clicks" + +#: views/campaigns/clicked.hbs:8 views/campaigns/view.hbs:65 +msgid "% of clicks" +msgstr "% de clicks" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:66 +msgid "% of messages" +msgstr "% de mensajes" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:69 +msgid "Aggregated clicks" +msgstr "Clics agregados" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Suscriptores que hicieron click en un enlace:" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Suscriptores que hicieron click en este enlace:" + +#: views/campaigns/clicked.hbs:13 views/campaigns/opened.hbs:7 +msgid "Stats by country" +msgstr "Estadísticas por país" + +#: views/campaigns/clicked.hbs:14 views/campaigns/opened.hbs:8 +msgid "Stats by device type" +msgstr "Estadísticas por dispositivo" + +#: views/campaigns/clicked.hbs:18 +msgid "First click time" +msgstr "Fecha del primer click" + +#: views/campaigns/clicked.hbs:19 +msgid "Click count" +msgstr "Número de clicks" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Información de quejas" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Suscriptores que se quejaron y fueron unsuscritos:" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Fecha de la queja" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:21 +msgid "Create RSS Campaign" +msgstr "Crear Campaña de RSS" + +#: views/campaigns/create-rss.hbs:5 views/campaigns/edit-rss.hbs:6 +msgid "" +"RSS campaign sets up a tracker against selected RSS feed address. Whenever a " +"new entry is found from this feed it is sent to selected list as an email " +"message." +msgstr "" +"Una campaña RSS detecta los cambios en el Canal RSS configurado. Cuando se " +"encuentra una nueva entrada de este canal es enviado a la lista seleccionada " +"como un mensaje de correo electrónico." + +#: views/campaigns/create-rss.hbs:7 views/campaigns/create-triggered.hbs:6 +#: views/campaigns/create.hbs:6 views/campaigns/edit-rss.hbs:9 +#: views/campaigns/edit-triggered.hbs:10 views/campaigns/edit.hbs:11 +msgid "Campaign Name" +msgstr "Nombre de la campaña" + +#: views/campaigns/create-rss.hbs:9 views/campaigns/create-triggered.hbs:8 +#: views/campaigns/create.hbs:8 views/campaigns/edit-rss.hbs:11 +#: views/campaigns/edit-triggered.hbs:12 views/campaigns/edit.hbs:13 +#: views/lists/create.hbs:8 views/lists/edit.hbs:11 +#: views/report-templates/partials/report-template-fields.hbs:4 +#: views/reports/partials/report-fields.hbs:4 views/templates/create.hbs:11 +#: views/templates/edit.hbs:10 views/triggers/create.hbs:9 +#: views/triggers/edit.hbs:10 +msgid "HTML is allowed" +msgstr "Se permite HTML" + +#: views/campaigns/create-rss.hbs:10 views/campaigns/create-triggered.hbs:9 +#: views/campaigns/create.hbs:9 views/campaigns/edit-rss.hbs:12 +#: views/campaigns/edit-triggered.hbs:13 views/campaigns/edit.hbs:14 +#: views/campaigns/view.hbs:6 views/triggers/create-select.hbs:6 +#: views/triggers/create.hbs:10 views/triggers/edit.hbs:12 +#: views/triggers/triggers.hbs:8 +msgid "List" +msgstr "Lista" + +#: views/campaigns/create-rss.hbs:11 views/campaigns/create-triggered.hbs:10 +#: views/campaigns/create-triggered.hbs:14 views/campaigns/create.hbs:10 +#: views/campaigns/create.hbs:14 views/campaigns/edit-rss.hbs:13 +#: views/campaigns/edit-triggered.hbs:14 views/campaigns/edit.hbs:15 +#: views/lists/fields/create.hbs:27 views/lists/fields/edit.hbs:28 +#: views/lists/segments/create.hbs:9 views/lists/segments/edit.hbs:10 +#: views/lists/segments/rule-create.hbs:7 views/lists/subscription/add.hbs:10 +#: views/lists/subscription/add.hbs:12 views/lists/subscription/edit.hbs:11 +#: views/lists/subscription/import-preview.hbs:5 +#: views/reports/partials/report-select-template.hbs:2 +#: views/subscription/partials/subscription-custom-fields.hbs:9 +#: views/templates/create.hbs:8 views/triggers/create-select.hbs:7 +#: views/triggers/create.hbs:17 views/triggers/create.hbs:20 +#: views/triggers/create.hbs:22 views/triggers/create.hbs:26 +#: views/triggers/edit.hbs:19 views/triggers/edit.hbs:22 +#: views/triggers/edit.hbs:24 views/triggers/edit.hbs:28 +msgid "Select" +msgstr "Seleccionar" + +#: views/campaigns/create-rss.hbs:12 views/campaigns/create-triggered.hbs:11 +#: views/campaigns/create.hbs:11 views/campaigns/edit-rss.hbs:14 +#: views/campaigns/edit-triggered.hbs:15 views/campaigns/edit.hbs:16 +#: views/triggers/create-select.hbs:8 views/triggers/create.hbs:11 +#: views/triggers/edit.hbs:13 +msgid "subscribers" +msgstr "suscriptores" + +#: views/campaigns/create-rss.hbs:13 views/campaigns/edit-rss.hbs:15 +msgid "RSS Feed Url" +msgstr "RSS Feed URL" + +#: views/campaigns/create-rss.hbs:14 views/campaigns/edit-rss.hbs:16 +msgid "" +"New entries from this RSS URL are sent out to list subscribers as email " +"messages" +msgstr "" +"Las nuevas entradas de esta dirección de URL de RSS se envían a los " +"suscriptores de la lista como mensajes de correo electrónico" + +#: views/campaigns/create-rss.hbs:15 views/campaigns/create-triggered.hbs:18 +#: views/campaigns/create.hbs:18 views/campaigns/edit-rss.hbs:18 +#: views/campaigns/edit-triggered.hbs:16 views/campaigns/edit.hbs:17 +#: views/campaigns/view.hbs:12 +msgid "Email \"from name\"" +msgstr "Email \"en nombre de (from)\"" + +#: views/campaigns/create-rss.hbs:16 views/campaigns/create-triggered.hbs:19 +#: views/campaigns/create.hbs:19 views/campaigns/edit-rss.hbs:19 +#: views/campaigns/edit-triggered.hbs:17 views/campaigns/edit.hbs:18 +#: views/settings.hbs:23 +msgid "This is the name your emails will come from" +msgstr "Este es el nombre con el que se enviaran sus correos electrónicos" + +#: views/campaigns/create-rss.hbs:17 views/campaigns/create-triggered.hbs:20 +#: views/campaigns/create.hbs:20 views/campaigns/edit-rss.hbs:20 +#: views/campaigns/edit-triggered.hbs:18 views/campaigns/edit.hbs:19 +#: views/campaigns/view.hbs:13 +msgid "Email \"from\" address" +msgstr "Correo electrónico \"De/From\"" + +#: views/campaigns/create-rss.hbs:18 views/campaigns/create-triggered.hbs:21 +#: views/campaigns/edit-rss.hbs:21 views/campaigns/edit-triggered.hbs:19 +#: views/settings.hbs:25 +msgid "This is the address people will send replies to" +msgstr "Este es el correo al que los contactos responderán" + +#: views/campaigns/create-rss.hbs:19 views/campaigns/create-triggered.hbs:24 +#: views/campaigns/create.hbs:26 views/campaigns/edit-rss.hbs:22 +#: views/campaigns/edit-triggered.hbs:22 views/campaigns/edit.hbs:25 +msgid "Disable opened tracking" +msgstr "Desactivar el seguimiento de Aperturas" + +#: views/campaigns/create-rss.hbs:20 views/campaigns/create-triggered.hbs:25 +#: views/campaigns/create.hbs:27 views/campaigns/edit-rss.hbs:23 +#: views/campaigns/edit-triggered.hbs:23 views/campaigns/edit.hbs:26 +msgid "Disable clicked tracking" +msgstr "Desactivar el seguimiento de Clics" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "Campaña Trigger creada" + +#: views/campaigns/create-triggered.hbs:12 views/campaigns/create.hbs:12 +#: views/campaigns/edit-triggered.hbs:7 views/campaigns/edit.hbs:7 +#: views/lists/fields/create.hbs:31 views/lists/fields/edit.hbs:33 +#: views/reports/reports.hbs:7 views/templates/create.hbs:13 +msgid "Template" +msgstr "Plantilla" + +#: views/campaigns/create-triggered.hbs:13 views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "Seleccionar plantilla" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "Selección de una plantilla para crear una copia de campaña de ella" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "" +"O, alternativamente, utilizar una URL como la fuente de contenido del " +"mensaje:" + +#: views/campaigns/create-triggered.hbs:17 views/campaigns/create.hbs:17 +#: views/campaigns/edit-triggered.hbs:26 views/campaigns/edit.hbs:29 +msgid "" +"If a message is sent then this URL will be POSTed to using Merge Tags as " +"POST body. Use this if you want to generate the HTML message yourself" +msgstr "" +"Si el mensaje es enviado, se realizará una petición POST a esta URL " +"utilizando los Merge Tags como parámetros. Utiliza esta opción si quieres " +"generar el mensaje HTML tu mismo" + +#: views/campaigns/create-triggered.hbs:22 views/campaigns/create.hbs:24 +#: views/campaigns/edit-triggered.hbs:20 views/campaigns/edit.hbs:23 +#: views/campaigns/view.hbs:15 +msgid "Email \"subject line\"" +msgstr "Email \"asunto\"" + +#: views/campaigns/create-triggered.hbs:23 views/campaigns/create.hbs:25 +#: views/campaigns/edit-triggered.hbs:21 views/campaigns/edit.hbs:24 +#: views/settings.hbs:27 +msgid "Keep it relevant and non-spammy" +msgstr "Mantener relevancia y no spam" + +#: views/campaigns/create.hbs:21 views/campaigns/edit.hbs:20 +msgid "" +"This is the address people will send replies to unless reply-to address is " +"set" +msgstr "" +"Esta es la dirección donde los usuarios enviarán respuestas a menos que " +"\"mail de respuesta\" sea establecido" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "Dirección de correo \"de respuesta\"" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "" +"Si se establece, esta es la dirección donde los usuarios enviarán respuestas" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "Información tiempo de entrega" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" +"Los suscriptores que recibieron el mensaje y no han sido bounces ni bajas:" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "Tiempo de entrega" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "Editar Campaña RSS" + +#: views/campaigns/edit-rss.hbs:7 views/campaigns/edit-triggered.hbs:8 +#: views/campaigns/edit.hbs:9 views/settings.hbs:4 views/users/account.hbs:6 +msgid "General Settings" +msgstr "Configuración general" + +#: views/campaigns/edit-rss.hbs:17 +msgid "" +"Use special merge tag [RSS_ENTRY] to mark the position for the RSS post " +"content. Additionally you can use any valid merge tag as well." +msgstr "" +"Usar especial tag [RSS_ENTRY] para marcar la posición del contenido del post " +"RSS. Además puedes utilizas más tags" + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:35 +msgid "Delete Campaign" +msgstr "Eliminar Camppaña" + +#: views/campaigns/edit-rss.hbs:25 views/campaigns/edit-triggered.hbs:28 +#: views/campaigns/edit.hbs:36 views/lists/edit.hbs:20 +#: views/lists/fields/edit.hbs:39 views/lists/forms/edit.hbs:33 +#: views/lists/forms/forms.hbs:12 views/lists/segments/edit.hbs:14 +#: views/lists/segments/rule-edit.hbs:38 views/lists/subscription/edit.hbs:18 +#: views/reports/edit.hbs:6 views/settings.hbs:99 views/templates/edit.hbs:12 +#: views/triggers/edit.hbs:30 views/users/account.hbs:18 +msgid "Update" +msgstr "Actualizar" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "Editar Campaña Activadas" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +#: routes/forms.js:143 +msgid "General" +msgstr "General" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:27 +msgid "Template Settings" +msgstr "Configuración de plantilla" + +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:28 +msgid "Template URL" +msgstr "URL de la plantilla" + +#: views/campaigns/edit.hbs:3 views/campaigns/edit.hbs:4 +#: views/campaigns/upload-attachment.hbs:3 +#: views/campaigns/upload-attachment.hbs:5 views/campaigns/view.hbs:3 +msgid "Edit Campaign" +msgstr "Modificar campaña" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:30 +msgid "Attachments" +msgstr "Archivos adjuntos" + +#: views/campaigns/edit.hbs:31 +msgid "File" +msgstr "Archivo" + +#: views/campaigns/edit.hbs:32 +msgid "Size" +msgstr "Tamaño" + +#: views/campaigns/edit.hbs:33 views/campaigns/view.hbs:68 +#: views/lists/fields/fields.hbs:12 views/lists/forms/forms.hbs:9 +#: views/lists/view.hbs:33 +msgid "No data available in table" +msgstr "No hay datos disponibles en la tabla" + +#: views/campaigns/edit.hbs:34 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "Añadir un adjunto" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "Información apertura" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "Los suscriptores que abrieron este mensaje:" + +#: views/campaigns/opened.hbs:12 +msgid "First open" +msgstr "Primera apertura" + +#: views/campaigns/opened.hbs:13 +msgid "Opened count" +msgstr "Recuento de aperturas" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "Información de bajas" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "Suscriptores que se han dado de baja" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:28 +#: views/lists/subscription/import.hbs:10 routes/lists.js:206 +msgid "Unsubscribed" +msgstr "Dado de baja" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "Subir" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "Visión de conjunto" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "Links" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "URL de Feed" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "Último control" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "No revisado aún" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "" +"Activar la campaña para empezar a comprobar si hay nuevos mensajes de Feed" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "Estatus RSS" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "Campaña de vista previa como" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "Añadir un nuevo usuario de prueba" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "Ningún miembro de la prueba aún, crear uno aquí" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "Ir" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:26 +msgid "Delivered" +msgstr "Entregado" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "Lista de Suscriptores que recibieron este boletín" + +#: views/campaigns/view.hbs:22 +msgid "Blacklisted" +msgstr "Lista negra" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who blacklisted by global blacklist" +msgstr "Lista de suscriptores que sin lista negra de lista negra mundial" + +#: views/campaigns/view.hbs:24 routes/lists.js:206 +msgid "Bounced" +msgstr "Rebotado" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who bounced" +msgstr "Lista de suscriptores que ha rebotado" + +#: views/campaigns/view.hbs:26 +msgid "Complaints" +msgstr "Quejas" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who complained for this message" +msgstr "Lista de suscriptores que se ha quejado por este boletín" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who unsubscribed after this message" +msgstr "Lista de suscriptores que se han dado de baja tras este boletín" + +#: views/campaigns/view.hbs:30 +msgid "Opened" +msgstr "Aperturas" + +#: views/campaigns/view.hbs:31 +msgid "List subscribers who opened this message" +msgstr "Lista de suscriptores que han abierto este boletín" + +#: views/campaigns/view.hbs:32 +msgid "Clicked" +msgstr "Clics" + +#: views/campaigns/view.hbs:33 views/campaigns/view.hbs:70 +msgid "List subscribers who clicked on a link" +msgstr "Lista de suscriptores que han hecho clic en este boletín" + +#: views/campaigns/view.hbs:34 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" +"¿Estás seguro? Esta acción empezará a enviar el boletín a la lista " +"seleccionada" + +#: views/campaigns/view.hbs:35 +msgid "Delay sending" +msgstr "Envío retrasado" + +#: views/campaigns/view.hbs:36 +msgid "hours" +msgstr "horas" + +#: views/campaigns/view.hbs:37 +msgid "minutes" +msgstr "minutos" + +#: views/campaigns/view.hbs:38 +msgid "Send to subscribers:" +msgstr "Enviar a suscriptores:" + +#: views/campaigns/view.hbs:39 +msgid "Are you sure? This action would reset scheduling" +msgstr "¿Estás seguro? Esta acción restablecerá la programación" + +#: views/campaigns/view.hbs:40 +msgid "Cancel" +msgstr "Cancelado" + +#: views/campaigns/view.hbs:41 +msgid "Sending scheduled" +msgstr "Envío programado" + +#: views/campaigns/view.hbs:42 views/campaigns/view.hbs:54 +msgid "Pause" +msgstr "Pausa" + +#: views/campaigns/view.hbs:43 routes/campaigns.js:253 +msgid "Sending" +msgstr "Enviando" + +#: views/campaigns/view.hbs:44 views/campaigns/view.hbs:48 +msgid "" +"Are you sure? This action would resume sending messages to the selected list" +msgstr "" +"¿Estás seguro? Esta acción reanudará el envío del boletín a la lista " +"seleccionada" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" +"¿Estás seguro? Esta acción reiniciará todas las estadísticas de progreso " +"actual" + +#: views/campaigns/view.hbs:46 +msgid "Resume" +msgstr "Reanudar" + +#: views/campaigns/view.hbs:47 views/campaigns/view.hbs:51 +msgid "Reset" +msgstr "Reiniciar" + +#: views/campaigns/view.hbs:50 +msgid "Continue" +msgstr "Continuar" + +#: views/campaigns/view.hbs:52 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" +"¡Todos los mensajes han sido enviados! Haz clic en \"Continuar\" si quieres " +"enviar la campaña a nuevos suscriptores" + +#: views/campaigns/view.hbs:53 +msgid "" +"Are you sure? This action would pause sending new entries in RSS feed as " +"email messages to the selected list" +msgstr "" +"¿Estás seguro? Esta acción pausará el envío de nuevas entradas RSS a la " +"lista seleccionada" + +#: views/campaigns/view.hbs:55 views/campaigns/view.hbs:59 +msgid "Campaign status:" +msgstr "Estado de la campaña:" + +#: views/campaigns/view.hbs:56 +msgid "ACTIVE" +msgstr "ACTIVO" + +#: views/campaigns/view.hbs:57 +msgid "" +"Are you sure? This action would start sending new entries in RSS feed as " +"email messages to the selected list" +msgstr "" +"¿Estás seguro? Esta acción empezará el envío del boletín a la lista " +"seleccionada" + +#: views/campaigns/view.hbs:58 +msgid "Activate" +msgstr "Activar" + +#: views/campaigns/view.hbs:60 +msgid "INACTIVE" +msgstr "INACTIVO" + +#: views/campaigns/view.hbs:61 +msgid "" +"This is a triggered campaign. Messages are only sent to subscribers that hit " +"some trigger that invokes this campaign" +msgstr "" +"Esta es una campaña encadenada (Trigger). Los boletines son enviados a " +"suscriptores que han desencadenado alguna acción de esta campaña." + +#: views/campaigns/view.hbs:62 +msgid "see more" +msgstr "ver más" + +#: views/campaigns/view.hbs:67 +msgid "List subscribers who clicked this link" +msgstr "Lista de suscriptores que ha hecho clic en este link" + +#: views/campaigns/view.hbs:71 +msgid "" +"Clicks are counted as unique subscribers that clicked on a specific link or " +"on any link (in aggregated view)" +msgstr "" +"Clics únicos, contados como único suscriptores hace clic en link específico " +"o en ningún link (vista agregada)" + +#: views/campaigns/view.hbs:72 +msgid "" +"If a new entry is found from campaign feed a new subcampaign is created of " +"that entry and it will be listed here" +msgstr "" +"Si una nueva entrada es en la campaña Feed, una nueva subcampaña de esta " +"entrada es creada y será listada aquí" + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "Cambiar contraseña" + +#: views/emails/password-reset-html.hbs:2 +#: views/emails/password-reset-text.hbs:2 +msgid "We have received a password change request for your Mailtrain account:" +msgstr "" +"Hemos recibido una solicitud de cambio de contraseña de su cuenta Mailtrain:" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "Restablecer la contraseña" + +#: views/emails/password-reset-html.hbs:4 +#: views/emails/password-reset-text.hbs:4 +msgid "" +"If you did not ask to change your password, then you can ignore this email " +"and your password will not be changed." +msgstr "" +"Si usted no pidió que cambie su contraseña, entonces puede ignorar este " +"mensaje y no se cambiará la contraseña." + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "Preferencias" + +#: views/emails/rss-html.hbs:2 views/emails/stationery-html.hbs:4 +#: views/emails/stationery-text.hbs:4 views/lists/forms/edit.hbs:20 +#: views/lists/subscription/edit.hbs:16 +#: views/subscription/partials/subscription-unsubscribe-form.hbs:2 +#: views/subscription/web-manage.mjml.hbs:3 +#: views/subscription/web-unsubscribe.mjml.hbs:1 +#: views/subscription/web-unsubscribe.mjml.hbs:2 routes/forms.js:223 +#: routes/lists.js:288 +msgid "Unsubscribe" +msgstr "Darse de baja" + +#: views/emails/rss-html.hbs:3 views/emails/stationery-html.hbs:5 +#: views/emails/stationery-text.hbs:5 +msgid "View this email in your browser" +msgstr "Ver este correo electrónico en su navegador" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "Hola [FIRST_NAME/Customer]," + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "Gracias," + +#: views/index.hbs:1 +msgid "List Management" +msgstr "Gestión de la lista" + +#: views/index.hbs:2 +msgid "" +"Mailtrain allows you to easily manage even very large lists. Million " +"subscribers? Not a problem. You can add subscribers manually, through the " +"API or import from a CSV file. All lists come with support for custom fields " +"and merge tags as well." +msgstr "" +"Mailtrain le permite administrar fácilmente listas muy grandes. ¿Millones de " +"suscriptores? No es un problema. Puede añadir manualmente los suscriptores, " +"a través de la API o importación de un archivo CSV. Todas las listas vienen " +"con soporte para los campos personalizados y se fusionan las etiquetas " +"también." + +#: views/index.hbs:3 views/index.hbs:7 views/index.hbs:10 views/index.hbs:13 +#: views/index.hbs:16 views/index.hbs:19 views/index.hbs:22 views/index.hbs:25 +#: views/index.hbs:28 +msgid "Show more" +msgstr "Mostrar más" + +#: views/index.hbs:4 views/lists/fields/create.hbs:3 +#: views/lists/fields/edit.hbs:3 views/lists/fields/fields.hbs:3 +#: views/lists/fields/fields.hbs:5 views/lists/view.hbs:6 +msgid "Custom Fields" +msgstr "Campos Personalizados" + +#: views/index.hbs:5 +msgid "" +"Text fields, numbers, drop downs or checkboxes, Mailtrain has them all. " +"Every custom field can be included in the generated newsletters through " +"merge tags." +msgstr "" +"Mailtrain tiene campos de texto, números, menús desplegables y casillas de " +"verificación. Cada campo personalizado se puede incluir en los boletines " +"generados a través de las etiquetas." + +#: views/index.hbs:6 +msgid "Mailtrain also supports custom forms." +msgstr "Mailtrain soporta formularios personalizados también." + +#: views/index.hbs:8 +msgid "List Segmentation" +msgstr "Lista Segmentada" + +#: views/index.hbs:9 +msgid "" +"Send messages only to list subscribers that match predefined segmentation " +"rules. No need to create separate lists with small differences." +msgstr "" +"Enviar mensajes sólo a la lista de suscriptores que cumple las de " +"segmentación predefinidas. No hay necesidad de crear listas separadas con " +"pequeñas diferencias." + +#: views/index.hbs:11 +msgid "RSS Campaigns" +msgstr "Campañas RSS" + +#: views/index.hbs:12 +msgid "" +"Setup Mailtrain to track RSS feeds and if a new entry is detected in a feed " +"then Mailtrain auto-generates a new campaign using entry data as message " +"contents and sends it to selected subscribers." +msgstr "" +"Configura Mailtrain para seguir el Fedd de RSS y si se detecta una nueva " +"entrada del feed, Mailtrain generará automáticamente una nueva campaña a " +"partir de los datos de la entrada como contenido del mensaje y lo enviará a " +"los suscriptores seleccionados." + +#: views/index.hbs:14 +msgid "GPG Encryption" +msgstr "Cifrado GPG" + +#: views/index.hbs:15 +msgid "" +"If a list has a custom field for a GPG Public Key set then subscribers can " +"upload their GPG public key to receive encrypted messages from the list." +msgstr "" +"Si una lista tiene un campo personalizado para una clave pública GPG " +"establecida, los suscriptores pueden actualizar su clave pública GPG para " +"recibir mensajes cifrados de la lista." + +#: views/index.hbs:17 +msgid "Click Stats" +msgstr "Estadísticas de clics" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" +"Tras enviar una campaña, comprobar las estadísticas de clic individuales " +"para cada enlace incluido en el mensaje." + +#: views/index.hbs:20 +msgid "Template Editors" +msgstr "Editores plantilla" + +#: views/index.hbs:21 +msgid "" +"Mailtrain ships with GrapeJS and Mosaico built in, two advanced template " +"editors. Mailtrain also offers a code editor if you prefer to handcraft the " +"HTML yourself." +msgstr "" +"Mailtrain envía con los incorporados GrapeJS y Mosaico, dos editores " +"avanzados de plantillas . Mailtrain también ofrece un editor de código, por " +"si prefieres modificar HTML." + +#: views/index.hbs:23 +msgid "Send via Any Provider" +msgstr "Enviar a través de cualquier proveedor" + +#: views/index.hbs:24 +msgid "" +"Mailtrain recommends SendPulse even though you " +"can use any provider that supports SMTP protocol to send out your " +"newsletters. Bounce and complaints handling via webhooks is supported for " +"SES, SparkPost, SendGrid and Mailgun, also for Postfix and ZoneMTA." +msgstr "" +"Mailtrain recomienda SendPulseaunque se puede " +"utilizar cualquier proveedor que soporte el protocolo SMTP para enviar sus " +"boletines de noticias. El manejo de rebotes y de quejas a través de WebHooks " +"es compatible para SES, SparkPost, SendGrid y Mailgun, también para Postfix " +"y ZoneMTA." + +#: views/index.hbs:26 lib/tools.js:137 +msgid "Automation" +msgstr "Automatización" + +#: views/index.hbs:27 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" +"Definir los factores desencadenantes de automatización para enviar boletines " +"específicos cuando un usuario activa el Trigger." + +#: views/index.hbs:29 +msgid "Donate to Author" +msgstr "Donar al autor" + +#: views/index.hbs:30 +msgid "Mailtrain is available under GPLv3 license and completely open source." +msgstr "" +"Mailtrain está disponible bajo licencia GPLv3 y la fuente es completamente " +"abierta (Open source)." + +#: views/index.hbs:31 +msgid "" +"If you really like Mailtrain or your business benefits from it financially " +"then I would really appreciate a small donation to keep the Mailtrain " +"development engines running. You can either use Bitcoin or PayPal for " +"donations. My Bitcoin wallet is" +msgstr "" +"Si realmente te gusta Mailtrain o su negocio se beneficia de ella " +"financieramente entonces yo realmente apreciaría una pequeña donación para " +"mantener los motores de desarrollo Mailtrain. También se puede usar Bitcoin " +"o PayPal para donaciones. Bitcoin es mi monedero." + +#: views/index.hbs:32 +msgid "Or Donate Using Paypal" +msgstr "O donar usando Paypal" + +#: views/index.hbs:33 +msgid "Official Mailtrain Partners" +msgstr "Socios oficiales de Mailtrain" + +#: views/index.hbs:34 +msgid "" +"A reliable SMTP server, easy integration, and 12,000 messages a month free" +msgstr "" +"Un servidor SMTP fiable, de fácil integración y envío de 12.000 mensajes " +"durante un mes gratis" + +#: views/index.hbs:35 +msgid "Free, open source mail server solution" +msgstr "" +"Gratis, solución de código abierto (open source) de servidor de correo " + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "Interruptor de navegación" + +#: views/layout.hbs:3 +msgid "Wiki" +msgstr "Wiki" + +#: views/layout.hbs:4 +msgid "Blog" +msgstr "Blog" + +#: views/layout.hbs:5 views/users/account.hbs:2 views/users/account.hbs:3 +msgid "Account" +msgstr "Cuenta" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "Ajustes" + +#: views/layout.hbs:8 views/users/api.hbs:2 views/users/api.hbs:3 +msgid "API" +msgstr "API" + +#: views/layout.hbs:9 +msgid "Log out" +msgstr "Cerrar sesión" + +#: views/layout.hbs:10 views/users/forgot.hbs:2 views/users/login.hbs:2 +#: views/users/login.hbs:3 views/users/login.hbs:9 views/users/reset.hbs:2 +msgid "Sign in" +msgstr "Registrarse" + +#: views/layout.hbs:11 +msgid "Self Hosted Newsletter App Built on Top of Nodemailer" +msgstr "Applicación de Newsletter \"autoalojada\" creada en base a Nodemailer?" + +#: views/layout.hbs:12 views/layout.hbs:14 +msgid "Source on GitHub" +msgstr "Fuente en GitHub" + +#: views/layout.hbs:13 +msgid "Subscribe to Our Newsletter" +msgstr "Suscríbete a nuestro boletín" + +#: views/lists/create.hbs:2 views/lists/edit.hbs:2 +#: views/lists/fields/create.hbs:2 views/lists/fields/edit.hbs:2 +#: views/lists/fields/fields.hbs:2 views/lists/forms/create.hbs:2 +#: views/lists/forms/edit.hbs:2 views/lists/forms/forms.hbs:2 +#: views/lists/lists.hbs:2 views/lists/lists.hbs:4 +#: views/lists/segments/create.hbs:2 views/lists/segments/edit.hbs:2 +#: views/lists/segments/rule-configure.hbs:2 +#: views/lists/segments/rule-create.hbs:2 views/lists/segments/rule-edit.hbs:2 +#: views/lists/segments/segments.hbs:2 views/lists/segments/view.hbs:2 +#: views/lists/subscription/add.hbs:2 views/lists/subscription/edit.hbs:2 +#: views/lists/subscription/import-failed.hbs:2 +#: views/lists/subscription/import-preview.hbs:2 +#: views/lists/subscription/import.hbs:2 views/lists/view.hbs:2 +#: lib/tools.js:125 routes/lists.js:59 +msgid "Lists" +msgstr "Listas" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:13 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "Crear lista" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "Nombre de Lista" + +#: views/lists/create.hbs:9 views/lists/edit.hbs:15 +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "Suscripción" + +#: views/lists/create.hbs:10 views/lists/edit.hbs:16 +msgid "Allow public users to subscribe themselves" +msgstr "Permitir que los usuarios públicos se suscriban a sí mismos " + +#: views/lists/create.hbs:11 views/lists/edit.hbs:17 +msgid "Unsubscription" +msgstr "Darse de baja" + +#: views/lists/create.hbs:12 views/lists/edit.hbs:18 +msgid "Select how an unsuscription request by subscriber is handled." +msgstr "Seleccionar como el suscriptor maneja la solicitud de baja." + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:8 +msgid "Edit List" +msgstr "Editar lista" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "Ver lista" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "ID de lista" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "Este es el ID de la lista que se muestra a los suscriptores" + +#: views/lists/edit.hbs:12 +msgid "Custom Form" +msgstr "Formulario personalizado" + +#: views/lists/edit.hbs:13 views/lists/forms/forms.hbs:11 +msgid "Default Mailtrain Form" +msgstr "Formulario por defecto de Mailtrain" + +#: views/lists/edit.hbs:14 +msgid "" +"The custom form used for this list. You can create a form here." +msgstr "" +"El formulario personalizado que se utiliza para esta lista. Puedes crear un " +"formulario aquí ." + +#: views/lists/edit.hbs:19 +msgid "Delete List" +msgstr "Eliminar lista" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "Crear campo" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "Crear campo personalizado" + +#: views/lists/fields/create.hbs:6 views/lists/fields/create.hbs:7 +#: views/lists/fields/edit.hbs:7 views/lists/fields/edit.hbs:8 +msgid "Field Name" +msgstr "Nombre de campo" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "Tipo de campo" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "Texto" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "Número" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "Web" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "Clave pública GPG" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "Texto de varias líneas" + +#: views/lists/fields/create.hbs:14 views/lists/fields/edit.hbs:15 +msgid "JSON" +msgstr "JSON" + +#: views/lists/fields/create.hbs:15 views/lists/fields/edit.hbs:16 +msgid "Date" +msgstr "Fecha" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "Fecha (MM/DD/YYYY)" + +#: views/lists/fields/create.hbs:17 views/lists/fields/edit.hbs:18 +#: lib/models/fields.js:26 +msgid "Date (DD/MM/YYYY)" +msgstr "Fecha (DD/MM/YYYY)" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "Cumpleaños" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "Cumpleaños (MM/DD)" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "Cumpleaños (DD/MM)" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "Agrupado" + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "Listas deplegables" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "Botones Radio " + +#: views/lists/fields/create.hbs:24 views/lists/fields/edit.hbs:25 +#: lib/models/fields.js:23 +msgid "Checkboxes" +msgstr "Checkboxes" + +#: views/lists/fields/create.hbs:25 views/lists/fields/edit.hbs:26 +msgid "Option for a group value" +msgstr "Opción para un valor de grupo" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "Grupo" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "Requerido para Opciones de Grupo" + +#: views/lists/fields/create.hbs:29 views/lists/fields/create.hbs:30 +#: views/lists/fields/edit.hbs:35 views/lists/fields/edit.hbs:36 +#: views/lists/fields/fields.hbs:9 +msgid "Default merge tag value" +msgstr "Valor de la etiqueta Merge por defecto" + +#: views/lists/fields/create.hbs:32 views/lists/fields/edit.hbs:34 +msgid "" +"For group elements like checkboxes you can control the appearance of the " +"merge tag with an optional template. The template uses handlebars syntax and " +"you can find all values from {{values}} array, for example " +"{{#each values}} {{this}} {{/each}}. If template is not defined " +"then multiple values are joined with commas. You can also use this template " +"to render JSON values (if the JSON is an array then the array is exposed as " +"values, otherwise you can access the JSON keys directly)." +msgstr "" +"Para los elementos de grupo como checkboxes tu puedes controlar la " +"apariencia de la etiqueta merge con una plantilla opcional. La plantilla " +"utiliza la sintaxis Handlebars y se pueden encontrar todos los valores de " +"{{values}} array, por ejemplo {{#each values}} {{this}} " +"{{/each}}. Si la plantilla no se define, varios valores se unen con " +"comas. También puedes utilizar esta plantilla para rederizar valores JSON " +"(si el JSON es un array, el array es expuesto como values, de " +"lo contrario puedes acceder directamente a las claves JSON)." + +#: views/lists/fields/create.hbs:33 views/lists/fields/edit.hbs:37 +msgid "Visible" +msgstr "Visible" + +#: views/lists/fields/create.hbs:34 +msgid "Add Field" +msgstr "Añadir Campo" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "Editar Campo" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "Editar campo personalizado" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "Volver a campos" + +#: views/lists/fields/edit.hbs:30 views/lists/fields/fields.hbs:8 +#: views/mosaico/editor.hbs:2 views/partials/merge-tag-reference.hbs:3 +msgid "Merge tag" +msgstr "Etiqueta Merge" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "Etiqueta Merge" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "Pon esta etiqueta en tu contenido:" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "Eliminar campo" + +#: views/lists/fields/fields.hbs:7 views/lists/view.hbs:26 +#: views/report-templates/partials/report-template-fields.hbs:5 +msgid "Type" +msgstr "Tipo" + +#: views/lists/fields/fields.hbs:10 views/lists/fields/fields.hbs:11 +#: views/lists/forms/edit.hbs:26 views/lists/forms/forms.hbs:8 +#: views/lists/segments/segments.hbs:8 views/lists/segments/view.hbs:12 +#: views/triggers/triggers.hbs:14 routes/campaigns.js:276 +#: routes/campaigns.js:568 routes/campaigns.js:657 routes/campaigns.js:706 +#: routes/lists.js:170 routes/lists.js:257 routes/report-templates.js:51 +#: routes/templates.js:170 routes/triggers.js:297 +msgid "Edit" +msgstr "Editar" + +#: views/lists/forms/create.hbs:3 views/lists/forms/edit.hbs:3 +#: views/lists/forms/forms.hbs:3 views/lists/forms/forms.hbs:5 +#: views/lists/view.hbs:5 +msgid "Custom Forms" +msgstr "Formularios personalizados" + +#: views/lists/forms/create.hbs:4 +msgid "Create Form" +msgstr "Crear formulario" + +#: views/lists/forms/create.hbs:5 views/lists/forms/forms.hbs:4 +msgid "Create Custom Form" +msgstr "Crear formulario personalizado" + +#: views/lists/forms/create.hbs:6 views/lists/forms/create.hbs:7 +#: views/lists/forms/edit.hbs:7 views/lists/forms/edit.hbs:8 +msgid "Form Name" +msgstr "Nombre de Formulario" + +#: views/lists/forms/create.hbs:8 +msgid "Add Form" +msgstr "Añadir Formulario" + +#: views/lists/forms/edit.hbs:4 +msgid "Edit Form" +msgstr "Editar formulario" + +#: views/lists/forms/edit.hbs:5 +msgid "Edit Custom Form" +msgstr "Editar Formulario personalizado" + +#: views/lists/forms/edit.hbs:6 +msgid "Back to forms" +msgstr "Volver a Formularios" + +#: views/lists/forms/edit.hbs:10 +msgid "Optional comments about this form" +msgstr "Compentarios opcionales sobre el formulario" + +#: views/lists/forms/edit.hbs:11 +msgid "Form Preview" +msgstr "Vista previa del formulario" + +#: views/lists/forms/edit.hbs:12 +msgid "" +"Note: These links are solely for a quick preview. If you submit a preview " +"form you'll get redirected to the list's default form." +msgstr "" +"Nota: Estos enlaces son únicamente para una vista rápida. Si seleccionas la " +"vista previa de un formulario se te redirecciona a la lista por defecto del " +"formulario." + +#: views/lists/forms/edit.hbs:13 views/lists/subscription/add.hbs:16 +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:4 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:4 +#: routes/forms.js:157 routes/lists.js:288 +msgid "Subscribe" +msgstr "Suscribirse" + +#: views/lists/forms/edit.hbs:14 +msgid "Confirm Subscription Notice" +msgstr "Aviso de Confirmación de suscripción" + +#: views/lists/forms/edit.hbs:15 +msgid "Confirm Unsubscription Notice" +msgstr "Aviso de Confirmación de baja" + +#: views/lists/forms/edit.hbs:16 +msgid "Subscribed Notice" +msgstr "Aviso Suscripción" + +#: views/lists/forms/edit.hbs:17 +msgid "Updated Notice" +msgstr "Aviso Actualización" + +#: views/lists/forms/edit.hbs:18 +msgid "Unsubscribed Notice" +msgstr "Aviso Baja" + +#: views/lists/forms/edit.hbs:19 +msgid "Manual Unsubscribe Notice" +msgstr "Aviso Manual de baja" + +#: views/lists/forms/edit.hbs:21 routes/forms.js:205 +msgid "Manage" +msgstr "Gestión" + +#: views/lists/forms/edit.hbs:22 +msgid "Manage Address" +msgstr "Gestión de dirección" + +#: views/lists/forms/edit.hbs:23 +msgid "Create a test user for additional options" +msgstr "Crear un usuario de prueba para opciones adicionales" + +#: views/lists/forms/edit.hbs:24 views/report-templates/create.hbs:3 +#: views/report-templates/edit.hbs:3 +#: views/report-templates/report-templates.hbs:3 views/templates/create.hbs:2 +#: views/templates/edit.hbs:2 views/templates/templates.hbs:2 +#: views/templates/templates.hbs:4 lib/tools.js:129 routes/templates.js:27 +msgid "Templates" +msgstr "Plantillas" + +#: views/lists/forms/edit.hbs:25 +msgid "Fields" +msgstr "Campos" + +#: views/lists/forms/edit.hbs:27 +msgid "Form Fields" +msgstr "Campos de formulario" + +#: views/lists/forms/edit.hbs:28 +msgid "Fields hidden on subscription page:" +msgstr "Campos ocultos de página de suscripción" + +#: views/lists/forms/edit.hbs:29 +msgid "Fields shown on subscription page:" +msgstr "Campos no ocultos de página de suscripción" + +#: views/lists/forms/edit.hbs:30 +msgid "Fields hidden on preferences page:" +msgstr "Campos ocultos de página de preferencias" + +#: views/lists/forms/edit.hbs:31 +msgid "Fields shown on preferences page:" +msgstr "Campos no ocultos de páginas de preferencias" + +#: views/lists/forms/edit.hbs:32 +msgid "Delete Form" +msgstr "Eliminar Formulario" + +#: views/lists/forms/forms.hbs:10 +msgid "The default form for this list is:" +msgstr "El formulario por defecto de esta lista es:" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "ID" + +#: views/lists/lists.hbs:7 views/reports/partials/report-fields.hbs:10 +msgid "Subscribers" +msgstr "Suscriptores" + +#: views/lists/segments/create.hbs:3 views/lists/segments/edit.hbs:3 +#: views/lists/segments/rule-configure.hbs:3 +#: views/lists/segments/rule-create.hbs:3 views/lists/segments/rule-edit.hbs:3 +#: views/lists/segments/segments.hbs:3 views/lists/segments/segments.hbs:5 +#: views/lists/segments/view.hbs:3 views/lists/view.hbs:7 +#: views/lists/view.hbs:14 +msgid "Segments" +msgstr "Segmentos" + +#: views/lists/segments/create.hbs:4 views/lists/segments/create.hbs:5 +#: views/lists/segments/rule-configure.hbs:4 +#: views/lists/segments/rule-create.hbs:4 views/lists/segments/rule-edit.hbs:4 +#: views/lists/segments/segments.hbs:4 +msgid "Create Segment" +msgstr "Crear Segmento" + +#: views/lists/segments/create.hbs:6 views/lists/segments/create.hbs:7 +#: views/lists/segments/edit.hbs:7 views/lists/segments/edit.hbs:8 +msgid "Segment Name" +msgstr "Nombre del Segmento" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "Combinación de reglas" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "Todas las reglas deben combinar" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "Ninguna regla debe combinar" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "Añadir segmento" + +#: views/lists/segments/edit.hbs:4 views/lists/segments/edit.hbs:5 +#: views/lists/segments/view.hbs:6 views/lists/view.hbs:12 +msgid "Edit Segment" +msgstr "Editar segmento" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "Volver a segmentos" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "Eliminar segmento" + +#: views/lists/segments/rule-configure.hbs:5 +#: views/lists/segments/rule-create.hbs:5 views/lists/segments/rule-edit.hbs:5 +#: views/lists/segments/view.hbs:4 +msgid "Create Rule" +msgstr "Crear regla" + +#: views/lists/segments/rule-configure.hbs:6 +#: views/lists/segments/rule-create.hbs:6 views/lists/segments/rule-edit.hbs:6 +#: views/lists/segments/view.hbs:10 +msgid "Rule" +msgstr "Regla" + +#: views/lists/segments/rule-configure.hbs:7 +#: views/lists/segments/rule-configure.hbs:8 +#: views/lists/segments/rule-configure.hbs:10 +#: views/lists/segments/rule-configure.hbs:13 +#: views/lists/segments/rule-configure.hbs:25 +#: views/lists/segments/rule-configure.hbs:30 +#: views/lists/segments/rule-edit.hbs:7 views/lists/segments/rule-edit.hbs:8 +#: views/lists/segments/rule-edit.hbs:10 views/lists/segments/rule-edit.hbs:15 +#: views/lists/segments/rule-edit.hbs:29 views/lists/segments/rule-edit.hbs:34 +#: views/lists/segments/view.hbs:11 +msgid "Value" +msgstr "Valor" + +#: views/lists/segments/rule-configure.hbs:9 +#: views/lists/segments/rule-edit.hbs:9 +msgid "" +"Use % for wildcard character, e.g. \"%test\" to match all values that end " +"with \"test\"" +msgstr "" +"Usar % para carácter comodín, por ejemplo \"%test\" para combinar todos los " +"valores que terminen en \"test\"" + +#: views/lists/segments/rule-configure.hbs:11 +#: views/lists/segments/rule-configure.hbs:14 +#: views/lists/segments/rule-configure.hbs:26 +#: views/lists/segments/rule-edit.hbs:11 views/lists/segments/rule-edit.hbs:16 +#: views/lists/segments/rule-edit.hbs:30 +msgid "Use exact match" +msgstr "Usar combinación exacta" + +#: views/lists/segments/rule-configure.hbs:12 +#: views/lists/segments/rule-configure.hbs:15 +#: views/lists/segments/rule-configure.hbs:27 +#: views/lists/segments/rule-edit.hbs:12 views/lists/segments/rule-edit.hbs:17 +#: views/lists/segments/rule-edit.hbs:31 +msgid "Use range match" +msgstr "Usar rango de combinación" + +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-edit.hbs:20 +msgid "Use relative range match" +msgstr "Usar rango relativo de combinación" + +#: views/lists/segments/rule-configure.hbs:17 +#: views/lists/segments/rule-configure.hbs:28 +#: views/lists/segments/rule-edit.hbs:13 views/lists/segments/rule-edit.hbs:18 +#: views/lists/segments/rule-edit.hbs:21 views/lists/segments/rule-edit.hbs:32 +msgid "From" +msgstr "De" + +#: views/lists/segments/rule-configure.hbs:18 +#: views/lists/segments/rule-configure.hbs:22 +#: views/lists/segments/rule-edit.hbs:22 views/lists/segments/rule-edit.hbs:26 +msgid "days" +msgstr "días" + +#: views/lists/segments/rule-configure.hbs:19 +#: views/lists/segments/rule-configure.hbs:23 +#: views/lists/segments/rule-edit.hbs:23 views/lists/segments/rule-edit.hbs:27 +msgid "before today" +msgstr "antes de hoy" + +#: views/lists/segments/rule-configure.hbs:20 +#: views/lists/segments/rule-configure.hbs:24 +#: views/lists/segments/rule-edit.hbs:24 views/lists/segments/rule-edit.hbs:28 +msgid "after today" +msgstr "después de hoy" + +#: views/lists/segments/rule-configure.hbs:21 +#: views/lists/segments/rule-configure.hbs:29 +#: views/lists/segments/rule-edit.hbs:14 views/lists/segments/rule-edit.hbs:19 +#: views/lists/segments/rule-edit.hbs:25 views/lists/segments/rule-edit.hbs:33 +msgid "to" +msgstr "para" + +#: views/lists/segments/rule-configure.hbs:31 +#: views/lists/segments/rule-edit.hbs:35 lib/models/segments.js:156 +#: lib/models/segments.js:418 +msgid "Selected" +msgstr "Seleccionado" + +#: views/lists/segments/rule-configure.hbs:32 +#: views/lists/segments/rule-edit.hbs:36 lib/models/segments.js:156 +#: lib/models/segments.js:418 +msgid "Not selected" +msgstr "No Seleccionado" + +#: views/lists/segments/rule-configure.hbs:33 +msgid "Add Rule" +msgstr "Añadir regla" + +#: views/lists/segments/rule-create.hbs:8 +#: views/lists/subscription/import.hbs:15 +#: views/reports/create-select-template.hbs:5 +#: views/triggers/create-select.hbs:9 +msgid "Next" +msgstr "Siguiente" + +#: views/lists/segments/rule-edit.hbs:37 +msgid "Delete Rule" +msgstr "Eliminar regla" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "Combinar" + +#: views/lists/segments/view.hbs:5 views/lists/view.hbs:13 +msgid "Segment" +msgstr "Segmento" + +#: views/lists/segments/view.hbs:7 +msgid "Match rules" +msgstr "Combinar reglas" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "Conjunto de suscriptores" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "mostrar" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "Añadir suscriptor" + +#: views/lists/subscription/add.hbs:5 +#: views/subscription/partials/subscription-custom-fields.hbs:1 +#: views/users/account.hbs:7 +msgid "Email Address" +msgstr "Dirección Email" + +#: views/lists/subscription/add.hbs:8 views/lists/subscription/edit.hbs:9 +#: views/settings.hbs:82 views/settings.hbs:97 +#: views/subscription/partials/subscription-custom-fields.hbs:6 +msgid "Begins with" +msgstr "Empieza por" + +#: views/lists/subscription/add.hbs:9 views/lists/subscription/edit.hbs:10 +msgid "" +"Insert a GPG public key that will be used to encrypt messages sent this " +"subscriber" +msgstr "" +"Insertar una clave pública GPG que será usada para encriptar mensajes " +"enviados a este suscriptor" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "Zona horaria" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "¿Es un usuario test?" + +#: views/lists/subscription/add.hbs:14 views/lists/subscription/edit.hbs:14 +msgid "" +"If checked then this subscription can be used for previewing campaign " +"messages" +msgstr "" +"Si está marcado, esta suscripción puede ser usada para previsualizar los " +"boletines" + +#: views/lists/subscription/add.hbs:15 +msgid "" +"This person will not receive a confirmation email so make sure that you have " +"permission to email them." +msgstr "" +"Esta persona no recibirá correo de confirmación para asegurarse de que tiene " +"permiso para enviarle boletines." + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "Editar suscriptor" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "Volver a la lista" + +#: views/lists/subscription/edit.hbs:6 +#: views/lists/subscription/import-preview.hbs:6 +#: views/subscription/partials/subscription-unsubscribe-form.hbs:1 +#: lib/helpers.js:42 lib/models/segments.js:11 +msgid "Email address" +msgstr "Dirección Email" + +#: views/lists/subscription/edit.hbs:17 +msgid "Delete Subscription" +msgstr "Eliminar suscripción" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "Importar estatus" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "Direcciones fallidas" + +#: views/lists/subscription/import-failed.hbs:6 +msgid "" +"Role-based addresses like postmaster@example.com are blocked when importing. " +"Subscribers with role-based email addresses can join your list using the " +"subscription form" +msgstr "" +"Direcciones basadas en roles como postmaster@example.com se bloquean cuando " +"se importan. Los suscriptores con correos electrónicos basados en roles " +"pueden unirse a la lista utilizando el formulario de suscripción." + +#: views/lists/subscription/import-failed.hbs:7 +msgid "see here" +msgstr "ver aquí" + +#: views/lists/subscription/import-failed.hbs:9 +msgid "Fail reason" +msgstr "Razón fallida" + +#: views/lists/subscription/import-preview.hbs:3 +#: views/lists/subscription/import-preview.hbs:4 +#: views/lists/subscription/import.hbs:3 views/lists/subscription/import.hbs:4 +msgid "Import subscribers" +msgstr "Importar suscriptores" + +#: views/lists/subscription/import-preview.hbs:10 views/users/api.hbs:27 +#: views/users/api.hbs:35 views/users/api.hbs:43 views/users/api.hbs:54 +#: views/users/api.hbs:62 views/users/api.hbs:70 +msgid "Example" +msgstr "Ejemplo" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "Empezar importación" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "Archivo CSV" + +#: views/lists/subscription/import.hbs:6 +msgid "CSV delimiter" +msgstr "Delimitador CSV" + +#: views/lists/subscription/import.hbs:7 +msgid "Categorize the imported subscribers as" +msgstr "Categorizar los suscriptores importados como" + +#: views/lists/subscription/import.hbs:8 routes/lists.js:206 +msgid "Subscribed" +msgstr "Suscritos" + +#: views/lists/subscription/import.hbs:9 +msgid "Regular subscriber addresses" +msgstr "Correos de suscriptores regulares" + +#: views/lists/subscription/import.hbs:11 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "Correos suprimidos que serán dados de baja en tu lista" + +#: views/lists/subscription/import.hbs:12 +msgid "Check imported emails" +msgstr "Revisar los correos importados" + +#: views/lists/subscription/import.hbs:13 views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "Activado" + +#: views/lists/subscription/import.hbs:14 views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "Desactivado" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "Formulario de suscripción" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "Acciones de Lista" + +#: views/lists/view.hbs:9 views/triggers/create-select.hbs:3 +#: views/triggers/create-select.hbs:4 views/triggers/create.hbs:3 +#: views/triggers/create.hbs:4 views/triggers/create.hbs:27 +#: views/triggers/triggers.hbs:3 +msgid "Create Trigger" +msgstr "Crear Trigger" + +#: views/lists/view.hbs:10 +msgid "Add Subscriber" +msgstr "Añadir suscriptor" + +#: views/lists/view.hbs:11 +msgid "Import Subscribers" +msgstr "Importar suscriptores" + +#: views/lists/view.hbs:15 +msgid "Create New Segment" +msgstr "Crear nuevo segmento" + +#: views/lists/view.hbs:16 +msgid "Filter" +msgstr "Filtrar" + +#: views/lists/view.hbs:17 +msgid "Subscriptions" +msgstr "Sucriptores" + +#: views/lists/view.hbs:18 +msgid "Imports" +msgstr "Importaciones" + +#: views/lists/view.hbs:25 routes/campaigns.js:255 routes/lists.js:300 +msgid "Finished" +msgstr "Terminado" + +#: views/lists/view.hbs:27 +msgid "Added" +msgstr "Añadido" + +#: views/lists/view.hbs:28 +msgid "Updated" +msgstr "Actualizado" + +#: views/lists/view.hbs:29 +msgid "Failed" +msgstr "Fallido" + +#: views/lists/view.hbs:31 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" +"¿Estás seguro? Esta acción sólo será llamada para resolver importaciones " +"estancadas" + +#: views/lists/view.hbs:32 +msgid "Restart" +msgstr "Reiniciar" + +#: views/mosaico/editor.hbs:1 views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "Etiqueta referencia Merge" + +#: views/mosaico/editor.hbs:4 +msgid "MOSAICO Responsive Email Designer" +msgstr "Diseñador de boletines responsive MOSAICO" + +#: views/mosaico/editor.hbs:5 +msgid "Sucessfully saved" +msgstr "Guardado con éxito" + +#: views/mosaico/editor.hbs:6 +msgid "An error occured while saving the document" +msgstr "Un error ocurrió mientras se guardaban los documentos" + +#: views/mosaico/editor.hbs:7 +msgid "Unsaved changes will be lost. Close now?" +msgstr "Los cambios no guardados se perderán. ¿Cerrar ahora?" + +#: views/mosaico/editor.hbs:8 views/mosaico/editor.hbs:9 +msgid "Tags" +msgstr "Etiquetas" + +#: views/partials/codeeditor.hbs:1 views/partials/grapejs.hbs:1 +#: views/partials/mosaico.hbs:1 views/partials/summernote.hbs:1 +msgid "Template content (HTML)" +msgstr "Contenido de plantilla (HTML)" + +#: views/partials/editor-navbar.hbs:1 +msgid "SAVE" +msgstr "GUARDAR" + +#: views/partials/editor-navbar.hbs:2 +msgid "SAVING" +msgstr "GUARDANDO" + +#: views/partials/editor-navbar.hbs:3 +msgid "CLOSE" +msgstr "CERRAR" + +#: views/partials/grapejs.hbs:2 +msgid "Open GrapeJS" +msgstr "Abrir GrapeJS" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "Vista previa de Toggle HTML" + +#: views/partials/html-to-text.hbs:1 +msgid "" +"To extract the text from HTML click here." +msgstr "" +"Para extraer el texto de HTML, haga clic aquí." + +#: views/partials/html-to-text.hbs:2 +msgid "" +"Please note that your existing plaintext in the field above will be " +"overwritten. This feature uses the Premailer API, a third party " +"service. Their Terms of Service and Privacy Policy apply." +msgstr "" +"Por favor, tenga en cuenta que el texto plano del campo superior será " +"sobreescrito. Esta función utiliza Premailer API, como servicio de " +"terceros. Sus Condiciones de servicio y política de privacidad se aplican." + +#: views/partials/html-to-text.hbs:3 +msgid "An error occurred while talking to the server" +msgstr "Ha ocurrido un error mientras se comunicaba con el servidor" + +#: views/partials/merge-tag-reference.hbs:2 +msgid "" +"Merge tags are tags that are replaced before sending out the message. The " +"format of the merge tag is the following: [TAG_NAME] or " +"[TAG_NAME/fallback] where fallback is an optional " +"text value used when TAG_NAME is empty." +msgstr "" +"Merge Tags son etiquetas que son remplazadas antes de enviar el boletín. El " +"formato de las etiquetas Merge es el siguiente [TAG_NAME] o " +"[TAG_NAME/fallback] donde fallback es el valor de " +"un texto opcional usado cuando TAG_NAME está vacío." + +#: views/partials/modal-carousel.hbs:1 +msgid "{{title}}" +msgstr "{{title}}" + +#: views/partials/mosaico.hbs:2 +msgid "Open Mosaico" +msgstr "Abrir Mosaico" + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "Contenido de la plantilla (texto plano)" + +#: views/report-templates/create.hbs:2 views/report-templates/edit.hbs:2 +#: views/report-templates/report-templates.hbs:2 +#: views/reports/create-select-template.hbs:2 views/reports/create.hbs:2 +#: views/reports/edit.hbs:2 views/reports/output.hbs:2 +#: views/reports/reports.hbs:2 views/reports/reports.hbs:5 +#: views/reports/view.hbs:2 lib/tools.js:144 routes/reports.js:31 +msgid "Reports" +msgstr "Informes" + +#: views/report-templates/create.hbs:4 views/report-templates/create.hbs:6 +#: views/report-templates/report-templates.hbs:4 views/templates/create.hbs:3 +#: views/templates/create.hbs:4 views/templates/create.hbs:12 +#: views/templates/templates.hbs:3 +msgid "Create Template" +msgstr "Crear plantilla" + +#: views/report-templates/create.hbs:5 routes/report-templates.js:231 +msgid "Create Report Template" +msgstr "Crear informe de plantilla" + +#: views/report-templates/edit.hbs:4 views/templates/edit.hbs:3 +#: views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "Editar plantilla" + +#: views/report-templates/edit.hbs:5 routes/report-templates.js:262 +msgid "Edit Report Template" +msgstr "Editar informe de plantilla" + +#: views/report-templates/edit.hbs:6 views/templates/edit.hbs:11 +msgid "Delete Template" +msgstr "Eliminar plantilla" + +#: views/report-templates/edit.hbs:7 +msgid "Update and Stay" +msgstr "Actualizar y permanecer" + +#: views/report-templates/edit.hbs:8 +msgid "Update and Leave" +msgstr "Actualizar y salir" + +#: views/report-templates/partials/report-template-fields.hbs:2 +msgid "Template Name" +msgstr "Nombre de plantilla" + +#: views/report-templates/partials/report-template-fields.hbs:6 +msgid "User selectable fields" +msgstr "Usar campos seleccionables" + +#: views/report-templates/partials/report-template-fields.hbs:7 +msgid "Data processing code" +msgstr "Código de procesamiento de datos" + +#: views/report-templates/partials/report-template-fields.hbs:8 +msgid "Rendering template" +msgstr "Plantilla de renderizado" + +#: views/report-templates/report-templates.hbs:5 +msgid "Blank" +msgstr "Vacío/En blanco" + +#: views/report-templates/report-templates.hbs:6 +msgid "All Subscribers" +msgstr "Todos los suscriptores" + +#: views/report-templates/report-templates.hbs:7 +msgid "Grouped Subscribers" +msgstr "Suscriptores agrupados" + +#: views/report-templates/report-templates.hbs:8 +msgid "Export List as CSV" +msgstr "Exportar lista como CSV" + +#: views/report-templates/report-templates.hbs:9 views/reports/reports.hbs:4 +#: routes/report-templates.js:29 +msgid "Report Templates" +msgstr "Plantillas de informes" + +#: views/reports/create-select-template.hbs:3 +#: views/reports/create-select-template.hbs:4 views/reports/create.hbs:3 +#: views/reports/create.hbs:4 views/reports/create.hbs:5 +#: views/reports/reports.hbs:3 routes/reports.js:81 +msgid "Create Report" +msgstr "Crear informe" + +#: views/reports/edit.hbs:3 views/reports/edit.hbs:4 routes/reports.js:151 +msgid "Edit Report" +msgstr "Editar informe" + +#: views/reports/edit.hbs:5 +msgid "Delete Report" +msgstr "Eliminar informe" + +#: views/reports/partials/report-fields.hbs:2 +msgid "Report Name" +msgstr "Nombre de informe" + +#: views/reports/partials/report-fields.hbs:8 +#: views/reports/partials/report-fields.hbs:11 +msgid "" +"Select a campaign in the table above by clicking on the respective row " +"number." +msgstr "" +"Seleccionar una campaña en la tabla superior haciendo clic en el respectivo " +"número de columna" + +#: views/reports/partials/report-select-template.hbs:1 +msgid "Report Template" +msgstr "Plantilla informe" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "Dirección de servicio (URL)" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "Introduzca la URL a la que puede acceder este servicio desde" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "Email Admin" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "" +"Introduce la dirección email que será usada como \"From\" para los envíos" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "Desactivar editor WYSIWYG" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "" +"Si está marcado, el editor de mensajes muestra el código HTML sin la vista " +"previa" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "Desactivar los mensajes de confirmación de suscripción" + +#: views/settings.hbs:12 +msgid "" +"If checked then do not send a confirmation message that states the " +"subscriber is now subscribed or unsubscribed. This does not disable double " +"opt-in messages." +msgstr "" +"Si está marcado, no envía mensaje de confirmación que indica que el abonado " +"está suscrito o dado de baja. Esto no deshabilita dobles mensajes opt-in." + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "ID restreo" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "Introduzca un código de seguimiento de Google Analytics" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "Reconocimiento de portada" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "Código HTML es mostrado en la sección de encabezado de la portada" + +#: views/settings.hbs:17 +msgid "Campaign defaults" +msgstr "Fallos en la campaña" + +#: views/settings.hbs:18 +msgid "Sender name" +msgstr "Nombre de Remitente" + +#: views/settings.hbs:19 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "Nombre de Remitente, por ejemplo: Compañía Asombrosa SL." + +#: views/settings.hbs:20 +msgid "Default address" +msgstr "Dirección por defecto" + +#: views/settings.hbs:21 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "" +"Dirección de contacto proporcionada, por ejemplo. Calle lugar cualquiera 1, " +"Madrid, España" + +#: views/settings.hbs:22 +msgid "Default \"from name\"" +msgstr "Nombre \"From\" por defecto" + +#: views/settings.hbs:24 +msgid "Default \"from\" email" +msgstr "Email \"From\" por defecto" + +#: views/settings.hbs:26 +msgid "Default \"subject line\"" +msgstr "\"Asunto\" por defecto" + +#: views/settings.hbs:28 +msgid "Default homepage (URL)" +msgstr "Home por defecto (URL)" + +#: views/settings.hbs:29 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "" +"URL para redireccionar a los usuarios suscritos, por ejemplo http://example." +"com/" + +#: views/settings.hbs:30 +msgid "Mailer Settings" +msgstr "Ajustes Remitente" + +#: views/settings.hbs:31 +msgid "These settings are required to send out e-mail messages" +msgstr "Estos ajustes son requeridos para enviar boletines" + +#: views/settings.hbs:32 +msgid "SMTP" +msgstr "SMTP" + +#: views/settings.hbs:33 +msgid "AWS SES" +msgstr "AWS SES" + +#: views/settings.hbs:34 +msgid "Use SMTP for sending mail" +msgstr "Utiliza SMTP para enviar boletines" + +#: views/settings.hbs:35 +msgid "Hostname" +msgstr "Nombre Host" + +#: views/settings.hbs:36 +msgid "Port" +msgstr "Puerto" + +#: views/settings.hbs:37 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "Puerto, por ejemplo 465. Autodetecta si es vacio" + +#: views/settings.hbs:38 +msgid "Encryption" +msgstr "Codificación" + +#: views/settings.hbs:39 +msgid "Disable SMTP authentication" +msgstr "Desactivar la autenticación SMTP" + +#: views/settings.hbs:40 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "Nombre de usuario" + +#: views/settings.hbs:41 +msgid "Username, eg. myaccount@example.com" +msgstr "Nombre de usuario, por ejemplo. nombre@ejemplo.com" + +#: views/settings.hbs:42 views/settings.hbs:43 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "Contraseña" + +#: views/settings.hbs:44 +msgid "Use SES API for sending mail" +msgstr "Utilizar la API de SES para enviar correo" + +#: views/settings.hbs:45 +msgid "Access Key" +msgstr "Clave de acceso" + +#: views/settings.hbs:46 +msgid "AWS Access Key Id" +msgstr "ID Clave de acceso AWS" + +#: views/settings.hbs:47 +msgid "Secret Key" +msgstr "Clave secreta" + +#: views/settings.hbs:48 +msgid "AWS Secret Access Key" +msgstr "Clave de acceso secreta AWS" + +#: views/settings.hbs:49 +msgid "Region" +msgstr "Región" + +#: views/settings.hbs:50 +msgid "Checking" +msgstr "Comprobación" + +#: views/settings.hbs:51 +msgid "Check Mailer config" +msgstr "Compruebe configuración de Remitente" + +#: views/settings.hbs:52 +msgid "Don't have an SMTP account yet? Create a free SendPulse account" +msgstr "" +"¿No tienes una cuenta SMTP todavía? Crear una cuenta gratuita en SendPulse" + +#: views/settings.hbs:53 +msgid "here" +msgstr "aquí" + +#: views/settings.hbs:54 +msgid "Advanced Mailer settings" +msgstr "Ajustes avanzados de Remitente" + +#: views/settings.hbs:55 +msgid "Log SMTP transactions" +msgstr "Registrar transacciones SMTP" + +#: views/settings.hbs:56 +msgid "Allow self-signed certificates" +msgstr "Permitir certificados con firma propia" + +#: views/settings.hbs:57 +msgid "Max connections" +msgstr "Número máximo de conexiones" + +#: views/settings.hbs:58 +msgid "The count of max connections, eg. 10" +msgstr "Recuento de conexiones máximo, por ejemplo. 10" + +#: views/settings.hbs:59 +msgid "" +"The count of maximum simultaneous connections to make against the SMTP " +"server (defaults to 5). This limit is per sending process." +msgstr "" +"Recuento del número máximo de conexiones simultáneas para hacer frente al " +"servidor SMTP (por defecto 5). Este límite es por proceso de envío." + +#: views/settings.hbs:60 +msgid "Max messages" +msgstr "Número máximo de mensajes" + +#: views/settings.hbs:61 +msgid "The count of max messages, eg. 100" +msgstr "El recuento de mensajes máximo, por ejemplo. 100" + +#: views/settings.hbs:62 +msgid "" +"The number of messages to send through a single connection before the " +"connection is closed and reopened (defaults to 100)" +msgstr "" +"El número de mensajes a enviar a través de una única conexión antes de la " +"conexión se cierra y se vuelve a abrir (por defecto 100)" + +#: views/settings.hbs:63 +msgid "Throttling" +msgstr "Regulador " + +#: views/settings.hbs:64 +msgid "Messages per hour eg. 1000" +msgstr "Mensajes por hora, por ejemplo 1000" + +#: views/settings.hbs:65 +msgid "" +"Maximum number of messages to send in an hour. Leave empty or zero for no " +"throttling. If your provider uses a different speed limit (messages/minute " +"or messages/second) then convert this limit into messages/hour (1m/s => " +"3600m/h). This limit is per sending process." +msgstr "" +"Número máximo de mensajes que se envían en una hora. Dejar en blanco o nulo " +"sin regulación. Si su proveedor utiliza un límite de velocidad diferente " +"(mensajes / minuto o mensajes / segundo) a continuación, convertir este " +"límite en mensajes/hora (1m/s => 3600m/h). Este límite es por proceso de " +"envío." + +#: views/settings.hbs:66 +msgid "VERP bounce handling" +msgstr "Manejo de rebote VERP" + +#: views/settings.hbs:67 +msgid "" +"Mailtrain is able to use VERP based routing to detect bounces. In this case " +"the message is sent to the recipient using a custom VERP address as the " +"return path of the message. If the message is not accepted a bounce email is " +"sent to this special VERP address and thus a bounce is detected." +msgstr "" +"Mailtrain es capaz de utilizar el enrutamiento basado VERP para detectar " +"rebotes. En este caso el mensaje se envía al destinatario mediante una " +"dirección VERP personalizada como la vía de retorno del mensaje. Si el " +"mensaje no se acepta un correo electrónico de rebote es enviado a esta " +"dirección especial VERP y por lo tanto se detecta un rebote." + +#: views/settings.hbs:68 +msgid "" +"To get VERP working you need to set up a DNS MX record that points to your " +"Mailtrain hostname. You must also ensure that Mailtrain VERP interface is " +"available from port 25 of your server (port 25 usually requires root user " +"privileges). This way if anyone tries to send email to someuser@verp-" +"hostname then the email should end up to this server." +msgstr "" +"ePara obtener el trabajo de VERP, necesitas crear un registro DNS MX que " +"apunta a su nombre de host Mailtrain. También debe asegurarse de que la " +"interfaz Mailtrain VERP está disponible desde el puerto 25 del servidor " +"(puerto 25 por lo general requiere privilegios de usuario root). De esta " +"manera si alguien trata de enviar un correo electrónico a someuser@verp-" +"hostname, el correo electrónico debe terminar en este servidor." + +#: views/settings.hbs:69 +msgid "" +"VERP usually only works if you are using your own SMTP server. Regular relay " +"services (SES, SparkPost, Gmail etc.) tend to remove the VERP address from " +"the message." +msgstr "" +"Por lo general VERP sólo funciona si está utilizando su propio servidor " +"SMTP. Servicios de transmisión regulares (SES, SparkPost, Gmail, etc.) " +"tienden a eliminar la dirección VERP del mensaje." + +#: views/settings.hbs:70 +msgid "Use VERP to catch bounces" +msgstr "Utilice VERP para rebotes" + +#: views/settings.hbs:71 +msgid "Server hostname" +msgstr "Nombre de Host del servidor" + +#: views/settings.hbs:72 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "El nombre de host del servidor VERP, por ejemplo. bounces.ejemplo.com" + +#: views/settings.hbs:73 +msgid "" +"VERP bounce handling server hostname. This hostname is used in the SMTP " +"envelope FROM address and the MX DNS records should point to this server" +msgstr "" +"Nombre de Servidor Host que maneja rebotes VERP. Este nombre de host se " +"utiliza en el FROM del sobre SMTP y los registros DNS MX deben señalar a " +"este servidor" + +#: views/settings.hbs:74 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" +"Servidor Host VERP que maneja rebotes no esta activado. Modifica el archivo " +"de configuración de tu servidor y reinicia el servidor para activarlo." + +#: views/settings.hbs:75 +msgid "GPG Signing" +msgstr "Firma GPG" + +#: views/settings.hbs:76 +msgid "" +"Only messages that are encrypted can be signed. Subsribers who have not set " +"up a GPG public key in their profile receive normal email messages. Users " +"with GPG key set receive encrypted messages and if you have signing key also " +"set, the messages are signed with this key." +msgstr "" +"Sólo los mensajes cifrados pueden ser firmados. Los suscriptores que no han " +"establecido una clave pública GPG en su perfil de recibirán mensajes de " +"correo electrónico normales. Los usuarios con claves GPG establecidad, " +"recibirán mensajes cifrados y si tiene clave de firma también establecida, " +"los mensajes estarán firmados con esa clave." + +#: views/settings.hbs:77 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" +"No utilice las teclas susceptibles aquí. La clave privada y la frase de " +"contraseña no están cifrados en la base de datos." + +#: views/settings.hbs:78 +msgid "Private Key Passphrase" +msgstr "Frase clave privada" + +#: views/settings.hbs:79 +msgid "Passphrase for the key if set" +msgstr "Frase de contraseña para la clave si se establece" + +#: views/settings.hbs:80 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "" +"Sólo rellene esto si su clave privada se cifra con una frase de contraseña" + +#: views/settings.hbs:81 +msgid "GPG Private Key" +msgstr "Clave privada GPG" + +#: views/settings.hbs:83 +msgid "" +"This value is optional. If you do not provide a private key GPG encrypted " +"messages are sent without signing." +msgstr "" +"Este valor es opcional. Si usted no proporciona una clave privada GPG los " +"mensajes cifrados se envían sin firma." + +#: views/settings.hbs:84 +msgid "DKIM Signing by ZoneMTA" +msgstr "Firma DKIM por ZoneMTA" + +#: views/settings.hbs:85 +msgid "" +"If you are using ZoneMTA then Mailtrain can provide a DKIM key for signing " +"all outgoing messages. Other services usually provide their own means to " +"DKIM sign your messages" +msgstr "" +"Si está utilizando ZoneMTA, Mailtrain puede proporcionar una clave DKIM para " +"la firma de todos los mensajes salientes. Otros servicios suelen " +"proporcionar sus propios medios para firmar sus mensajes DKIM" + +#: views/settings.hbs:86 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" +"No utilice las teclas susceptibles aquí. La clave privada no está cifrada en " +"la base de datos." + +#: views/settings.hbs:87 +msgid "ZoneMTA DKIM API Key" +msgstr "CLAVE de API DKIM ZoneMTA" + +#: views/settings.hbs:88 +msgid "Some secret value" +msgstr "Algunos valores secretos" + +#: views/settings.hbs:89 +msgid "" +"Secret value known to ZoneMTA for requesting DKIM key information. If this " +"value was generated by the Mailtrain installation script then you can keep " +"it as it is" +msgstr "" +"Valor secreto conocido a ZoneMTA para solicitar información clave DKIM. Si " +"este valor se genera mediante el script de instalación Mailtrain entonces se " +"puede mantener como está" + +#: views/settings.hbs:90 +msgid "DKIM domain" +msgstr "dominio DKIM" + +#: views/settings.hbs:91 +msgid "Domain name for the DKIM key" +msgstr "nombre de dominio para la clave DKIM" + +#: views/settings.hbs:92 +msgid "Leave blank to use the sender email address domain" +msgstr "" +"Dejar en blanco para usar el dominio de dirección de correo electrónico del " +"remitente" + +#: views/settings.hbs:93 views/settings.hbs:94 +msgid "DKIM key selector" +msgstr "Selector Clave de DKIM" + +#: views/settings.hbs:95 +msgid "Signing is disabled without a valid selector value" +msgstr "Firma está desactivada y sin un valor de selección válido" + +#: views/settings.hbs:96 +msgid "DKIM Private Key" +msgstr "Clave privada DKIM" + +#: views/settings.hbs:98 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" +"Este valor es opcional. Si usted no proporciona una clave privada a " +"continuación, los mensajes no se firman." + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:1 +#: views/subscription/mail-already-subscribed-text.hbs:1 +#: lib/models/subscriptions.js:171 lib/models/subscriptions.js:892 +msgid "Email address already registered" +msgstr "Dirección de correo ya registrada" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:2 +#: views/subscription/mail-already-subscribed-text.hbs:2 +msgid "" +"We have received a subscription request. Your email address is however " +"already registered." +msgstr "" +"Hemos recibido una solicitud de suscripción. Su dirección de correo " +"electrónico está sin embargo ya registrada." + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:3 +#: views/subscription/mail-already-subscribed-text.hbs:3 +msgid "" +"If you received this email by mistake, simply delete it. Your existing " +"subscription won't be affected." +msgstr "" +"Si ha recibido este mensaje por error, simplemente elimínalo. Su " +"subscripción no se verá afectada." + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:4 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:3 +msgid "If you want to modify your subscription then you can " +msgstr "Si desea modificar su suscripción, puedes" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:5 +#: views/subscription/mail-already-subscribed-text.hbs:5 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:4 +#: views/subscription/mail-subscription-confirmed-text.hbs:4 +msgid "manage your preferences" +msgstr "administrar tus preferencias" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:6 +#: views/subscription/mail-already-subscribed-text.hbs:6 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:5 +#: views/subscription/mail-subscription-confirmed-text.hbs:5 +#: views/users/login.hbs:10 +msgid "or" +msgstr "o" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:7 +#: views/subscription/mail-already-subscribed-text.hbs:7 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:6 +#: views/subscription/mail-subscription-confirmed-text.hbs:6 +msgid "unsubscribe here" +msgstr "darte de baja aquí" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:8 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:7 +#: views/subscription/web-confirm-subscription-notice.mjml.hbs:3 +#: views/subscription/web-confirm-unsubscription-notice.mjml.hbs:3 +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:4 +#: views/subscription/web-subscribed-notice.mjml.hbs:4 +#: views/subscription/web-unsubscribed-notice.mjml.hbs:3 +#: views/subscription/web-updated-notice.mjml.hbs:3 +msgid "Return to our website" +msgstr "Vuelvea nuestra web" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:9 +#: views/subscription/mail-already-subscribed-text.hbs:8 +#: views/subscription/mail-confirm-address-change-html.mjml.hbs:4 +#: views/subscription/mail-confirm-address-change-text.hbs:4 +#: views/subscription/mail-confirm-subscription-html.mjml.hbs:4 +#: views/subscription/mail-confirm-subscription-text.hbs:4 +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:4 +#: views/subscription/mail-confirm-unsubscription-text.hbs:4 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:8 +#: views/subscription/mail-subscription-confirmed-text.hbs:7 +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:5 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:5 +msgid "For questions about this list, please contact:" +msgstr "" +"Para preguntas acerca de esta lista, por favor, póngase en contacto con " +"nosotros en:" + +#: views/subscription/mail-already-subscribed-text.hbs:4 +#: views/subscription/mail-subscription-confirmed-text.hbs:3 +msgid "If you want to modify your subscription then you can:" +msgstr "Si desea modificar su suscripción a continuación, puedes:" + +#: views/subscription/mail-confirm-address-change-html.mjml.hbs:1 +#: views/subscription/mail-confirm-address-change-text.hbs:1 +msgid "Please Confirm Subscription Address Change" +msgstr "Por favor, confirma el campo de Dirección de correo de Suscripción" + +#: views/subscription/mail-confirm-address-change-html.mjml.hbs:2 +#: views/subscription/mail-confirm-address-change-text.hbs:2 +msgid "Yes, subscribe this email address to the list" +msgstr "Sí, suscribir esta dirección de correo electrónico a la lista" + +#: views/subscription/mail-confirm-address-change-html.mjml.hbs:3 +#: views/subscription/mail-confirm-subscription-html.mjml.hbs:3 +msgid "" +"If you received this email by mistake, simply delete it. You won't be " +"subscribed if you don't click the confirmation link above." +msgstr "" +"Si ha recibido este mensaje por error, simplemente elimínalo. No será " +"suscrito si no hace clic en el enlace de confirmación anterior." + +#: views/subscription/mail-confirm-address-change-text.hbs:3 +#: views/subscription/mail-confirm-subscription-text.hbs:3 +msgid "" +"If you received this email by mistake, simply delete it. You won't be " +"subscribed unless you click the confirmation link above." +msgstr "" +"Si ha recibido este mensaje por error, simplemente elimínalo. No va a estar " +"suscrito a menos que haga clic en el enlace de confirmación anterior." + +#: views/subscription/mail-confirm-subscription-html.mjml.hbs:1 +#: views/subscription/mail-confirm-subscription-text.hbs:1 +#: views/subscription/mail-confirm-unsubscription-text.hbs:1 +#: routes/subscription.js:431 +msgid "Please Confirm Subscription" +msgstr "Por favor, confirme la suscripción" + +#: views/subscription/mail-confirm-subscription-html.mjml.hbs:2 +#: views/subscription/mail-confirm-subscription-text.hbs:2 +msgid "Yes, subscribe me to this list" +msgstr "Sí, suscribirme a la lista" + +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:1 +msgid "Please Confirm Unsubscription" +msgstr "Por favor, confirma la baja" + +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:2 +#: views/subscription/mail-confirm-unsubscription-text.hbs:2 +msgid "Yes, unsubscribe me from this list" +msgstr "Sí, darme de baja de esta lista" + +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:3 +msgid "" +"If you received this email by mistake, simply delete it. You won't be " +"unsubscribed if you don't click the confirmation link above." +msgstr "" +"Si ha recibido este mensaje por error, simplemente elimínalo. No será dado " +"de baja si no hace clic en el enlace de confirmación anterior." + +#: views/subscription/mail-confirm-unsubscription-text.hbs:3 +msgid "" +"If you received this email by mistake, simply delete it. You won't be " +"unsubscribed unless you click the confirmation link above." +msgstr "" +"Si ha recibido este mensaje por error, simplemente elimínalo. No será dado " +"de baja a menos de hacer clic en el enlace de confirmación anterior." + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-subscription-confirmed-text.hbs:1 +#: views/subscription/web-subscribed-notice.mjml.hbs:1 +msgid "Subscription Confirmed" +msgstr "Suscripción confirmada" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed" +msgstr "Su suscripción a nuestra lista ha sido confirmada" + +#: views/subscription/mail-subscription-confirmed-text.hbs:2 +#: views/subscription/web-subscribed-notice.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed." +msgstr "Su suscripción a nuestra lista ha sido confirmada." + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:1 +msgid "You Are Now Unsubscribed" +msgstr "Tu estás dado de baja ahora" + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:2 +msgid "We have removed your email address from our list" +msgstr "Hemos eliminado su correo electrónico de nuestra lista" + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:3 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:3 +msgid "If you unsubscribed by mistake, you can re-subscribe at:" +msgstr "Si te has dado de baja por error, puedes volverte a suscribir en:" + +#: views/subscription/mail-unsubscription-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "Hemos eliminado su correo electrónico de nuestra lista." + +#: views/subscription/partials/subscription-custom-fields.hbs:2 +msgid "want to change it?" +msgstr "quieres cambiarla?" + +#: views/subscription/partials/subscription-custom-fields.hbs:5 +msgid "Download signature verification key" +msgstr "Descargar clave de verificación de firma" + +#: views/subscription/partials/subscription-custom-fields.hbs:7 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "" +"Inserta tu clave GPG pública aquí para encriptar mensajes enviados a tu " +"dirección" + +#: views/subscription/partials/subscription-custom-fields.hbs:8 +msgid "optional" +msgstr "opcional" + +#: views/subscription/partials/subscription-flash-messages.hbs:1 +#: views/subscription/partials/subscription-flash-messages.hbs:3 +msgid "Warning!" +msgstr "¡Advertencia!" + +#: views/subscription/partials/subscription-flash-messages.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "" +"Si JavaScript no está activada, no se envia ningún mensaje de confirmación" + +#: views/subscription/partials/subscription-flash-messages.hbs:4 +msgid "JavaScript must be enabled in order for this form to work" +msgstr "JavaScript debe estar habilitado para esta forma de funcionamiento" + +#: views/subscription/partials/subscription-manage-address-form.hbs:1 +msgid "Existing Email Address" +msgstr "Correo electrónico existente" + +#: views/subscription/partials/subscription-manage-address-form.hbs:2 +msgid "New Email Address" +msgstr "Nuevo Correo electrónico" + +#: views/subscription/partials/subscription-manage-address-form.hbs:3 +msgid "Your new email address" +msgstr "Tu nuevo correo electrónico" + +#: views/subscription/partials/subscription-manage-address-form.hbs:4 +msgid "" +"You will receive a confirmation request to your new email address that you " +"need to accept before your email is actually changed" +msgstr "" +"Recibirás un email a tu nuevo correo electrónico con una solicitud de " +"confirmación. Tras aceptarla el correo será cambiado definitivamente." + +#: views/subscription/partials/subscription-manage-address-form.hbs:5 +#: views/subscription/web-manage-address.mjml.hbs:2 +msgid "Update Email Address" +msgstr "Actualizar el correo electrónico" + +#: views/subscription/partials/subscription-manage-form.hbs:1 +#: views/subscription/web-manage.mjml.hbs:2 +msgid "Update Profile" +msgstr "Actualizar perfil" + +#: views/subscription/partials/subscription-subscribe-form.hbs:1 +#: views/subscription/web-subscribe.mjml.hbs:2 +#: views/subscription/widget-subscribe.hbs:1 +msgid "Subscribe to list" +msgstr "Suscrito a la lista" + +#: views/subscription/web-confirm-subscription-notice.mjml.hbs:1 +#: views/subscription/web-confirm-unsubscription-notice.mjml.hbs:1 +#: views/subscription/widget-subscribe.hbs:4 +msgid "Almost Finished" +msgstr "Casi terminado" + +#: views/subscription/web-confirm-subscription-notice.mjml.hbs:2 +#: views/subscription/widget-subscribe.hbs:5 +msgid "" +"We need to confirm your email address. To complete the subscription process, " +"please click the link in the email we just sent you." +msgstr "" +"Necesitamos confirmar tu dirección de correo electrónico. Para completar el " +"proceso de suscripción, por favor haz clic en el enlace del correo " +"electrónico que acabamos de enviarte." + +#: views/subscription/web-confirm-unsubscription-notice.mjml.hbs:2 +msgid "" +"We need to confirm your email address. To complete the unsubscription " +"process, please click the link in the email we just sent you." +msgstr "" +"Necesitamos confirmar tu dirección de correo electrónico. Para completar el " +"proceso de darse de baja, por favor haga clic en el enlace del correo " +"electrónico que acabamos de enviarte." + +#: views/subscription/web-manage-address.mjml.hbs:1 +msgid "Update Your Email Address" +msgstr "Actualizar su correo electrónico" + +#: views/subscription/web-manage.mjml.hbs:1 +msgid "Update Your Preferences" +msgstr "Actualizar sus preferencias" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:1 +msgid "Online Unsubscription Is Not Possible" +msgstr "Darse de baja online no es posible" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:2 +msgid "Please contact us at" +msgstr "Por favor, contácta con nosotros en" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:3 +msgid "to get removed from the list" +msgstr "ser eliminado de la lista" + +#: views/subscription/web-subscribe.mjml.hbs:1 +msgid "Subscribe to List" +msgstr "Suscribirse a la lista" + +#: views/subscription/web-subscribed-notice.mjml.hbs:3 +msgid "Thank you for subscribing!" +msgstr "¡Gracias por suscribirte!" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:1 +msgid "Unsubscribe Successful" +msgstr "Dado de baja con éxito" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:2 +msgid "You have been removed from:" +msgstr "Has sido eliminado de:" + +#: views/subscription/web-updated-notice.mjml.hbs:1 +msgid "Profile Updated" +msgstr "Perfeil actualizado" + +#: views/subscription/web-updated-notice.mjml.hbs:2 +msgid "Your profile information has been updated." +msgstr "La información de tu perfil ha sido actualizada" + +#: views/subscription/widget-subscribe.hbs:2 +msgid "Sending ..." +msgstr "Enviando" + +#: views/subscription/widget-subscribe.hbs:3 +msgid "It looks like you are already subscribed to this list." +msgstr "Parece que tu ya estabas suscrito a esta lista" + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "Nombre de plantilla" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "Nombre de esta plantilla, por ejemplo Newsletter" + +#: views/templates/create.hbs:7 +msgid "HTML Editor" +msgstr "Editor HTML" + +#: views/templates/create.hbs:10 views/templates/edit.hbs:9 +msgid "Optional comments about this template" +msgstr "Comentario opcional sobre esta plantilla" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "Volver a plantillas" + +#: views/triggers/create-select.hbs:2 views/triggers/create.hbs:2 +#: views/triggers/edit.hbs:2 views/triggers/triggered.hbs:2 +#: views/triggers/triggers.hbs:2 views/triggers/triggers.hbs:4 +msgid "Automation Triggers" +msgstr "Automatización Trigger " + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "Seleccionar una lista para el Trigger" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "Nombre de Trigger" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "Nombre para este Trigger, por ejemplo: Suscritores inactivos" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "Comentarios opcionales sobre el Trigger" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "Regla Trigger" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "Trigger encendido" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "días después:" + +#: views/triggers/create.hbs:16 views/triggers/create.hbs:21 +#: views/triggers/edit.hbs:18 views/triggers/edit.hbs:23 +msgid "Event" +msgstr "Evento" + +#: views/triggers/create.hbs:18 views/triggers/create.hbs:19 +#: views/triggers/create.hbs:25 views/triggers/edit.hbs:20 +#: views/triggers/edit.hbs:21 views/triggers/edit.hbs:27 +msgid "Campaign" +msgstr "Campaña" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "Acción Trigger" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "Enviar campaña" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "Edit Trigger" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "Volver a Triggers" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "Trigger está activado" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "Eliminar Trigger" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "Encadenados (Triggered)" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "Suscriptores Encadenados (Triggered)" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "Suscriptores que han provocado que este Trigger se encienda" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "Hora de Trigger" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "Trigger" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "Camapaña Objetivo" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "Cuenta Trigger" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "Esta cuenta esta gestionada mediante LDAP." + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "Dirección mail asociada" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "Tu dirección email" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you loose your password" +msgstr "" +"Este email es usado para recuperar una cuenta en caso de que no recuerdes la " +"contraseña" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "Cambio de contraseña" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "" +"Tu sólo necesitas completar este formulario si quieres cambiar la contraseña " +"actual" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "Contraseña actual" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "Nueva contraseña" + +#: views/users/account.hbs:16 views/users/reset.hbs:8 +msgid "Confirm Password" +msgstr "Confirmar contraseña" + +#: views/users/account.hbs:17 views/users/reset.hbs:9 +msgid "Confirm New Password" +msgstr "Confirmar nueva contraseña" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "" +"¿Estás seguro? El reinicio invalidará el token existente en este momento." + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "¿Estás seguro?" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "Restablecer token de Acceso" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "Token de Acceso generado" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "Token de acceso personal:" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "Token de Acceso no generado aún" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "Notas sobre la API" + +#: views/users/api.hbs:11 +msgid "" +"API response is a JSON structure with error and data properties. If the response error has a value set then " +"the request failed." +msgstr "" +"Respuesta API es una estructura JSON con error y data propiedades. Si la respuesta error tiene un valor " +"establecido la petición ha fallado." + +#: views/users/api.hbs:12 +msgid "" +"You need to define proper Content-Type when making a request. " +"You can either use application/x-www-form-urlencoded for normal " +"form data or application/json for a JSON payload. Using " +"multipart/form-data is not supported." +msgstr "" +"Necesitas definir correctamente Content-Type cuando se hace una " +"petición. Tu puedes usar application/x-www-form-urlencoded para " +"formulario de datos normal o application/json para carga JSON. " +"El uso de multipart/form-data no es soportado." + +#: views/users/api.hbs:13 +msgid "Add subscription" +msgstr "Añadir suscriptción" + +#: views/users/api.hbs:14 +msgid "" +"This API call either inserts a new subscription or updates existing. Fields " +"not included are left as is, so if you update only LAST_NAME value, then " +"FIRST_NAME is kept untouched for an existing subscription." +msgstr "" +"Esta API llama a insertar un nuevo suscriptor o actualizar existente. Campos " +"no incluidos no se modifican, pero si quieres actualizar solo el valor de " +"LAST_NAME, FIRST_NAME no se modifica para suscripciones existentes." + +#: views/users/api.hbs:15 views/users/api.hbs:17 views/users/api.hbs:30 +#: views/users/api.hbs:32 views/users/api.hbs:38 views/users/api.hbs:40 +#: views/users/api.hbs:46 views/users/api.hbs:57 views/users/api.hbs:59 +#: views/users/api.hbs:65 views/users/api.hbs:67 +msgid "arguments" +msgstr "razón/argumento" + +#: views/users/api.hbs:16 views/users/api.hbs:31 views/users/api.hbs:39 +#: views/users/api.hbs:47 views/users/api.hbs:58 views/users/api.hbs:66 +msgid "your personal access token" +msgstr "tu token personal de acceso" + +#: views/users/api.hbs:18 views/users/api.hbs:33 views/users/api.hbs:41 +msgid "subscriber's email address" +msgstr "Correo de Suscriptor" + +#: views/users/api.hbs:19 views/users/api.hbs:34 views/users/api.hbs:42 +#: views/users/api.hbs:61 views/users/api.hbs:69 +msgid "required" +msgstr "Obligatorio" + +#: views/users/api.hbs:20 +msgid "subscriber's first name" +msgstr "Nombre de suscriptor" + +#: views/users/api.hbs:21 +msgid "subscriber's last name" +msgstr "Apellidos de suscriptor" + +#: views/users/api.hbs:22 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" +"la zona horaria del suscriptor (por ejemplo. \"Europa / Tallin\", \"PST\" o " +"\"UTC\"). Si no se establece, por defecto es \"UTC\"" + +#: views/users/api.hbs:23 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" +"Valor de campo personalizado. Utilice sí / no para la opción de los valores " +"del grupo (casillas de verificación, radios, menús desplegables)" + +#: views/users/api.hbs:24 +msgid "Additional POST arguments" +msgstr "Los argumentos adicionales del POST" + +#: views/users/api.hbs:25 +msgid "" +"set to \"yes\" if you want to make sure the email is marked as subscribed " +"even if it was previously marked as unsubscribed. If the email was already " +"unsubscribed/blocked then subscription status is not changed" +msgstr "" +"Marca \"Sí\" si desea asegurarse de que el correo electrónico se marca como " +"suscrito incluso si fue previamente marcado como baja. Si el correo " +"electrónico ya fue dado de baja o bloqueado, el estado de suscripción no se " +"cambia" + +#: views/users/api.hbs:26 +msgid "" +"set to \"yes\" if you want to send confirmation email to the subscriber " +"before actually marking as subscribed" +msgstr "" +"Marca \"Sí\" si desea enviar un correo electrónico de confirmación al " +"suscriptor antes de que realmente se marque como suscrito" + +#: views/users/api.hbs:28 +msgid "Remove subscription" +msgstr "Eliminar suscripción" + +#: views/users/api.hbs:29 +msgid "This API call marks a subscription as unsubscribed" +msgstr "Esta llamada a la API marca una suscripción como dado de baja" + +#: views/users/api.hbs:36 +msgid "Delete subscription" +msgstr "Eliminar suscripción" + +#: views/users/api.hbs:37 +msgid "This API call deletes a subscription" +msgstr "Esta llamada a la API elimina una suscripción" + +#: views/users/api.hbs:44 +msgid "Get list of blacklisted emails" +msgstr "Obtener la lista de correos electrónicos de la lista negra" + +#: views/users/api.hbs:45 +msgid "This API call get list of blacklisted emails." +msgstr "" +"Esta llamada a la API consigue la lista de correos electrónicos de la lista " +"negra." + +#: views/users/api.hbs:48 +msgid "Start position" +msgstr "Posición de salida" + +#: views/users/api.hbs:49 +msgid "optional, default 0" +msgstr "opcional, por defecto 0" + +#: views/users/api.hbs:50 +msgid "limit emails count in response" +msgstr "cantidad límite de emails en la respuesta" + +#: views/users/api.hbs:51 +msgid "optional, default 10000" +msgstr "opcional, por defecto 10000" + +#: views/users/api.hbs:52 +msgid "filter by part of email" +msgstr "filtrar por correo electrónico" + +#: views/users/api.hbs:53 +msgid "optional, default ''" +msgstr "opcional, por defecto ''" + +#: views/users/api.hbs:56 +msgid "This API call either add emails to blacklist" +msgstr "Esta llamada a la API añade correos electrónicos a la lista negra" + +#: views/users/api.hbs:60 views/users/api.hbs:68 +msgid "email address" +msgstr "Correo electrónico" + +#: views/users/api.hbs:63 +msgid "Delete email from blacklist" +msgstr "Eliminar correo electrónico de la lista negra" + +#: views/users/api.hbs:64 +msgid "This API call either delete emails from blacklist" +msgstr "" +"Esta llamada a la API ya sea borrado mensajes de correo electrónico de la " +"lista negra" + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "Restablecimiento de contraseña" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "¿Quieres restablecer tu contraseña?" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "Las cuentas se gestionan a través de LDAP." + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "Restablecer la contraseña" + +#: views/users/forgot.hbs:7 +msgid "" +"Please provide the username or email address that you used when you signed " +"up for your Mailtrain account." +msgstr "" +"Por favor proporcione el nombre de usuario o correo electrónico que utilizó " +"cuando se inscribió en su cuenta Mailtrain." + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "" +"Te enviaremos un correo electrónico que te permitirá restablecer tu " +"contraseña." + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "Nombre de usuario o correo electrónico" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "Envía un correo electrónico de verificación" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "Recuérdame" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "¿Se te olvidó tu contraseña?" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "Cambiar la contraseña nueva" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "Por favor, introduzca una contraseña nueva." + +#: lib/editor-helpers.js:17 routes/templates.js:95 +msgid "Could not find template with specified ID" +msgstr "No se pudo encontrar la plantilla con el ID especificado" + +#: lib/editor-helpers.js:33 routes/archive.js:145 routes/campaigns.js:131 +#: routes/campaigns.js:284 routes/campaigns.js:379 routes/campaigns.js:427 +#: routes/campaigns.js:467 routes/campaigns.js:844 routes/campaigns.js:867 +#: routes/campaigns.js:886 routes/campaigns.js:908 routes/triggers.js:146 +msgid "Could not find campaign with specified ID" +msgstr "No se pudo encontrar campaña con el ID especificado" + +#: lib/editor-helpers.js:47 routes/editorapi.js:308 +msgid "Invalid resource type" +msgstr "Tipo de recurso no válido" + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "Mal código de estado %s" + +#: lib/helpers.js:33 +msgid "URL that points to the unsubscribe page" +msgstr "URL que apunta a la página de darse de baja" + +#: lib/helpers.js:36 +msgid "URL that points to the preferences page of the subscriber" +msgstr "URL que apunta a la página de preferencias del suscriptor" + +#: lib/helpers.js:39 +msgid "URL to preview the message in a browser" +msgstr "URL para obtener una vista previa del mensaje en un navegador" + +#: lib/helpers.js:45 lib/models/segments.js:31 +msgid "First name" +msgstr "Nombre" + +#: lib/helpers.js:48 lib/models/segments.js:35 +msgid "Last name" +msgstr "Apellidos" + +#: lib/helpers.js:51 +msgid "Full name (first and last name combined)" +msgstr "Nombre completo (nombre y apellidos)" + +#: lib/helpers.js:54 +msgid "Unique ID that identifies the recipient" +msgstr "ID único que identifica al destinatario" + +#: lib/helpers.js:57 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "ID único que identifica la lista prevista para esta campaña" + +#: lib/helpers.js:60 +msgid "Unique ID that identifies current campaign" +msgstr "ID único que identifica la campaña actual" + +#: lib/helpers.js:68 lib/helpers.js:80 +msgid "content from an RSS entry" +msgstr "Contenido de una entrada RSS" + +#: lib/helpers.js:71 +msgid "RSS entry title" +msgstr "Título de la entrada RSS" + +#: lib/helpers.js:74 +msgid "RSS entry date" +msgstr "Fecha de entrada RSS" + +#: lib/helpers.js:77 +msgid "RSS entry link" +msgstr "Enlace de entrada RSS" + +#: lib/helpers.js:83 +msgid "RSS entry summary" +msgstr "Resumen de entrada RSS" + +#: lib/helpers.js:86 +msgid "RSS entry image URL" +msgstr "URL de la imagen de entrada RSS" + +#: lib/mailer.js:245 +msgid "Invalid mail transport" +msgstr "Modo fe envío de correo no válido" + +#: lib/models/campaigns.js:105 lib/models/campaigns.js:132 +#: lib/models/campaigns.js:205 lib/models/campaigns.js:328 +#: lib/models/campaigns.js:590 lib/models/campaigns.js:723 +msgid "Missing Campaign ID" +msgstr "Falta el ID de campaña" + +#: lib/models/campaigns.js:241 +msgid "Emtpy or too large attahcment" +msgstr "Adjunto vacío o de tamaño superior al posible" + +#: lib/models/campaigns.js:408 lib/models/campaigns.js:600 +msgid "Campaign Name must be set" +msgstr "El nombre de la campaña se debe ajustar" + +#: lib/models/campaigns.js:412 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "La URL del RSS se debe establecer y debe ser una URL válida" + +#: lib/models/campaigns.js:568 +msgid "Selected template not found" +msgstr "La plantilla seleccionada no se encuentra" + +#: lib/models/campaigns.js:924 +msgid "Invalid or missing message ID" +msgstr "Falta ID del mensaje o es inválido" + +#: lib/models/campaigns.js:1065 +msgid "Unrecognized message status" +msgstr "Estado del mensaje no reconocido" + +#: lib/models/confirmations.js:27 +msgid "Could not store confirmation data" +msgstr "No se pudo almacenar datos de confirmación" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "Desplegable" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "Fecha (MM/DD/YYY)" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "valor para la representación personalizada JSON" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "Opción" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/forms.js:46 lib/models/lists.js:83 lib/models/lists.js:118 +#: lib/models/lists.js:232 lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:79 lib/models/subscriptions.js:387 +#: lib/models/subscriptions.js:563 lib/models/subscriptions.js:654 +#: lib/models/subscriptions.js:707 lib/models/subscriptions.js:770 +#: lib/models/subscriptions.js:813 +msgid "Missing List ID" +msgstr "Falta el ID de la lista" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "El campo Opción requiere que un grupo está seleccionado" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "Falta el campo ID" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "El campo Nombre debe establecerse" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "Campo personalizado no encontrado" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "Desconocido el tipo de columna %s" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "Falta el nombre de la columna" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "Falta el ID de la lista" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "ID proporcionado de la lista no es encontrado" + +#: lib/models/forms.js:70 lib/models/forms.js:113 lib/models/forms.js:201 +#: lib/models/forms.js:291 +msgid "Missing Form ID" +msgstr "Falta ID del Formulario" + +#: lib/models/forms.js:121 lib/models/forms.js:205 +msgid "Form Name must be set" +msgstr "El Nombre del Formulario se debe ajustar" + +#: lib/models/forms.js:307 +msgid "Custom form not found" +msgstr "Formulario personalizado no encontrado" + +#: lib/models/links.js:337 routes/campaigns.js:533 routes/campaigns.js:581 +#: routes/campaigns.js:621 routes/campaigns.js:671 services/sender.js:305 +msgid "Campaign not found" +msgstr "Campaña no encontrada" + +#: lib/models/links.js:345 routes/lists.js:181 services/sender.js:312 +msgid "List not found" +msgstr "Lista no encontrada" + +#: lib/models/links.js:353 +msgid "Subscription not found" +msgstr "Suscripción no encontrada" + +#: lib/models/lists.js:134 lib/models/lists.js:178 +msgid "List Name must be set" +msgstr "El nombre de la lista debe ser ajustado" + +#: lib/models/lists.js:261 +msgid "Missing List CID" +msgstr "Falta CID de la lista" + +#: lib/models/report-templates.js:26 lib/models/report-templates.js:70 +#: lib/models/report-templates.js:142 +msgid "Missing report template ID" +msgstr "Falta ID de plantilla informe" + +#: lib/models/report-templates.js:77 +msgid "Report template name must be set" +msgstr "El nombre de la plantilla informe se debe ajustar" + +#: lib/models/reports.js:40 lib/models/reports.js:110 lib/models/reports.js:188 +msgid "Missing report ID" +msgstr "Falta ID de informe " + +#: lib/models/reports.js:116 +msgid "Report name must be set" +msgstr "Nombre del informe se debe ajustar" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "País de registro" + +#: lib/models/segments.js:19 lib/models/triggers.js:12 +msgid "Sign up date" +msgstr "Fecha de inscripción" + +#: lib/models/segments.js:23 lib/models/triggers.js:16 +msgid "Latest open" +msgstr "Última apertura" + +#: lib/models/segments.js:27 lib/models/triggers.js:20 +msgid "Latest click" +msgstr "Último clic" + +#: lib/models/segments.js:69 lib/models/segments.js:216 +#: lib/models/segments.js:256 lib/models/segments.js:278 +msgid "Missing Segment ID" +msgstr "Falta el ID de segmento" + +#: lib/models/segments.js:85 lib/models/segments.js:549 +#: lib/models/segments.js:658 +msgid "Segment not found" +msgstr "El segmento no se encuentra" + +#: lib/models/segments.js:146 lib/models/segments.js:147 +#: lib/models/segments.js:408 lib/models/segments.js:409 +msgid "%s days after today" +msgstr "%s días después de hoy" + +#: lib/models/segments.js:146 lib/models/segments.js:147 +#: lib/models/segments.js:408 lib/models/segments.js:409 +msgid "%s days before today" +msgstr "%s días antes de hoy" + +#: lib/models/segments.js:148 lib/models/segments.js:410 +msgid "today" +msgstr "hoy" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "Tipo de regla de segmento no válido" + +#: lib/models/segments.js:289 lib/models/segments.js:454 routes/segments.js:266 +#: routes/segments.js:300 routes/segments.js:370 routes/segments.js:381 +msgid "Selected segment not found" +msgstr "El segmento seleccionado no se encuentra" + +#: lib/models/segments.js:294 lib/models/segments.js:459 routes/segments.js:272 +#: routes/segments.js:306 routes/segments.js:387 +msgid "Invalid rule type" +msgstr "Tipo de regla no válida" + +#: lib/models/segments.js:358 lib/models/segments.js:434 +#: lib/models/segments.js:524 +msgid "Missing Rule ID" +msgstr "Falta el ID de la regla" + +#: lib/models/segments.js:374 +msgid "Specified rule not found" +msgstr "La regla especificada no se encontra" + +#: lib/models/segments.js:385 +msgid "Specified segment not found" +msgstr "El segmento especificado no se encontra" + +#: lib/models/segments.js:445 +msgid "Selected rule not found" +msgstr "La regla seleccionada no se encontra" + +#: lib/models/subscriptions.js:254 lib/models/subscriptions.js:284 +msgid "Missing Subbscription ID" +msgstr "Falta el ID de Suscripción" + +#: lib/models/subscriptions.js:312 +msgid "Missing Subbscription email address" +msgstr "Falta el correo electrónico de suscripción" + +#: lib/models/subscriptions.js:391 +msgid "Missing Subscription ID" +msgstr "Falta el ID de suscripción" + +#: lib/models/subscriptions.js:567 lib/models/subscriptions.js:817 +msgid "Missing subscription ID" +msgstr "Falta el ID de suscripción" + +#: lib/models/subscriptions.js:658 lib/models/subscriptions.js:711 +#: lib/models/subscriptions.js:747 +msgid "Missing Import ID" +msgstr "Falta el ID de importación" + +#: lib/models/subscriptions.js:839 +msgid "Unknown subscription ID" +msgstr "No se conoce el ID de suscripción" + +#: lib/models/subscriptions.js:844 routes/subscription.js:638 +msgid "Nothing seems to be changed" +msgstr "Nada parece haber sido cambiado" + +#: lib/models/subscriptions.js:910 routes/subscription.js:472 +#: routes/subscription.js:544 routes/subscription.js:580 +#: routes/subscription.js:696 routes/subscription.js:771 +msgid "Subscription not found in this list" +msgstr "La suscripción no se encuentra en esta lista" + +#: lib/models/templates.js:26 lib/models/templates.js:100 +#: lib/models/templates.js:144 +msgid "Missing Template ID" +msgstr "Falta el ID de plantilla" + +#: lib/models/templates.js:55 lib/models/templates.js:104 +msgid "Template Name must be set" +msgstr "El nombre de la plantilla se debe ajustar" + +#: lib/models/triggers.js:29 +msgid "Has Opened" +msgstr "Ha abierto" + +#: lib/models/triggers.js:32 +msgid "Has Clicked" +msgstr "Ha hecho clic" + +#: lib/models/triggers.js:35 +msgid "Not Opened" +msgstr "No ha abierto" + +#: lib/models/triggers.js:38 +msgid "Not Clicked" +msgstr "No ha hecho clic" + +#: lib/models/triggers.js:175 lib/models/triggers.js:212 +msgid "Missing or invalid list ID" +msgstr "Falta ID de lista o es inválido" + +#: lib/models/triggers.js:179 lib/models/triggers.js:264 +msgid "Days in the past are not allowed" +msgstr "No se permiten días pasados" + +#: lib/models/triggers.js:183 lib/models/triggers.js:204 +#: lib/models/triggers.js:268 lib/models/triggers.js:289 +msgid "Missing or invalid trigger rule" +msgstr "Falta Regla encadenante o es inválida" + +#: lib/models/triggers.js:190 lib/models/triggers.js:275 +msgid "Invalid subscription configuration" +msgstr "La configuración de suscripción no es válida" + +#: lib/models/triggers.js:197 lib/models/triggers.js:282 +msgid "Invalid campaign configuration" +msgstr "Configuración de la campaña no es válida" + +#: lib/models/triggers.js:200 lib/models/triggers.js:285 +msgid "A campaing can not be a target for itself" +msgstr "Una campaña no puede ser un objetivo por sí mismo" + +#: lib/models/triggers.js:233 +msgid "Could not store trigger row" +msgstr "No se pudo almacenar el trigger" + +#: lib/models/triggers.js:250 +msgid "Missing or invalid Trigger ID" +msgstr "Falta ID de Trigger o es inválido" + +#: lib/models/triggers.js:317 +msgid "Missing Trigger ID" +msgstr "Falta ID de Trigger" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "No se pudo almacenar fila de usuario" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "El correo electrónico debe estar configurado" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "No se ha podido comprobar los datos de usuario" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" +"No se puede cambiar el correo electrónico como otro usuario con la misma " +"dirección de correo electrónico que ya existe" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "Contraseña actual incorrecta" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "La nueva contraseña no se ha establecido" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "Las contraseñas no coinciden" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "El ID de usuario no se ha establecido" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "El nombre de usuario debe configurarse" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "Solicitud de cambio de contraseña de correo electrónico" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "Falta el nombre de usuario o restablecer Token" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "Nueva contraseña inválida" + +#: lib/passport.js:40 +msgid "%s logged out" +msgstr "%s desconectado" + +#: lib/passport.js:53 +msgid "Failed to authenticate user" +msgstr "Error de autenticación de usuario" + +#: lib/passport.js:69 +msgid "Logged in as %s" +msgstr "Conectado como %s" + +#: lib/passport.js:128 +msgid "Incorrect username or password" +msgstr "Nombre de usuario o contraseña incorrecta" + +#: lib/subscription-mail-helpers.js:28 +msgid "%s: Subscription Confirmed" +msgstr "%s: Suscripción Confirmada" + +#: lib/subscription-mail-helpers.js:39 +msgid "%s: Email Address Already Registered" +msgstr "%s: Correo electrónico registrado" + +#: lib/subscription-mail-helpers.js:49 +msgid "%s: Please Confirm Email Change in Subscription" +msgstr "%s: Por favor confirma el cambio de correo electrónico de suscripción" + +#: lib/subscription-mail-helpers.js:59 +msgid "%s: Please Confirm Subscription" +msgstr "%s: Por favor confirma la Suscripción" + +#: lib/subscription-mail-helpers.js:69 +msgid "%s: Please Confirm Unsubscription" +msgstr "%s: Por favor confirma la Baja" + +#: lib/subscription-mail-helpers.js:76 +msgid "%s: Unsubscription Confirmed" +msgstr "%s: Baja confirmada :(" + +#: lib/tools.js:154 +msgid "Blocked email address \"%s\"" +msgstr "Correo electrónico bloqueado \"%s\"" + +#: lib/tools.js:163 +msgid "Invalid email address \"%s\"." +msgstr "Correo electrónico inválido \"%s\"." + +#: lib/tools.js:166 +msgid "MX record not found for domain" +msgstr "Registro MX para el dominio no encontrado" + +#: lib/tools.js:169 +msgid "Address domain not found" +msgstr "No se ha encontrado dominio de la dirección" + +#: lib/tools.js:172 +msgid "Address domain name is required" +msgstr "Se requiere el nombre de dominio" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:230 +msgid "Not Found" +msgstr "No encontrado" + +#: routes/archive.js:121 services/sender.js:451 +msgid "Received status code %s from %s" +msgstr "Código de estado recibido %s de %s" + +#: routes/archive.js:153 routes/campaigns.js:894 +msgid "Attachment not found" +msgstr "Adjunto no encontrado" + +#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:35 +#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:14 +#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20 +#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23 +#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75 +#: routes/users.js:120 +msgid "Need to be logged in to access restricted content" +msgstr "Tienes que estar registrado para acceder a contenido restringido" + +#: routes/campaigns.js:117 +msgid "Could not create campaign" +msgstr "No se pudo crear la campaña" + +#: routes/campaigns.js:120 +msgid "Campaign “%s” created" +msgstr "Campaña “%s” creada" + +#: routes/campaigns.js:209 +msgid "Campaign settings updated" +msgstr "Configuración de la campaña actualizada" + +#: routes/campaigns.js:211 +msgid "Campaign settings not updated" +msgstr "La configuración de la campaña no se ha actualizado" + +#: routes/campaigns.js:227 routes/campaigns.js:744 +msgid "Campaign deleted" +msgstr "Campaña eliminada" + +#: routes/campaigns.js:229 routes/campaigns.js:746 +msgid "Could not delete specified campaign" +msgstr "No se pudo eliminar la campaña especificada" + +#: routes/campaigns.js:248 +msgid "Idling" +msgstr "Inactivo/No usado" + +#: routes/campaigns.js:251 +msgid "Scheduled" +msgstr "Programado" + +#: routes/campaigns.js:257 +msgid "Paused" +msgstr "Pausado" + +#: routes/campaigns.js:259 +msgid "Inactive" +msgstr "Inactivo" + +#: routes/campaigns.js:261 +msgid "Active" +msgstr "Activo" + +#: routes/campaigns.js:263 +msgid "Other" +msgstr "Otros" + +#: routes/campaigns.js:421 +msgid "Unknown status selector" +msgstr "Estado seleccionado no conocido" + +#: routes/campaigns.js:762 +msgid "Scheduled sending" +msgstr "Envío programado" + +#: routes/campaigns.js:764 +msgid "Could not schedule sending" +msgstr "No se puede programar el envío" + +#: routes/campaigns.js:776 +msgid "Sending resumed" +msgstr "Envío reanudado" + +#: routes/campaigns.js:778 +msgid "Could not resume sending" +msgstr "No se pudo reanudar el envío" + +#: routes/campaigns.js:790 +msgid "Sending reset" +msgstr "Envío reiniciado" + +#: routes/campaigns.js:792 +msgid "Could not reset sending" +msgstr "No se ha podido reiniciar el envío" + +#: routes/campaigns.js:804 routes/campaigns.js:832 +msgid "Sending paused" +msgstr "Envío pausado" + +#: routes/campaigns.js:806 routes/campaigns.js:834 +msgid "Could not pause sending" +msgstr "No se ha podido pausar el envío" + +#: routes/campaigns.js:818 +msgid "Sending activated" +msgstr "Envío activado" + +#: routes/campaigns.js:820 +msgid "Could not activate sending" +msgstr "No se ha podido activar el envío" + +#: routes/campaigns.js:855 +msgid "Attachment uploaded" +msgstr "Adjunto subido" + +#: routes/campaigns.js:857 +msgid "Could not store attachment" +msgstr "No se ha podido almacenar el adjunto" + +#: routes/campaigns.js:874 +msgid "Attachment deleted" +msgstr "Adjunto eliminado" + +#: routes/campaigns.js:876 +msgid "Could not delete attachment" +msgstr "No se ha podido eliminar el adjunto" + +#: routes/editorapi.js:41 +msgid "Invalid editor name" +msgstr "Nombre de Editor inválido" + +#: routes/editorapi.js:237 routes/editorapi.js:275 +msgid "Method not supported" +msgstr "Método no soportado" + +#: routes/editorapi.js:352 +msgid "Invalid resource type or ID" +msgstr "Tipo de recurso o ID no válido" + +#: routes/fields.js:28 routes/fields.js:64 routes/fields.js:118 +#: routes/forms.js:31 routes/forms.js:63 routes/forms.js:94 +#: routes/segments.js:28 routes/segments.js:59 routes/segments.js:102 +#: routes/segments.js:151 routes/segments.js:223 routes/segments.js:255 +#: routes/segments.js:289 routes/segments.js:336 routes/segments.js:359 +msgid "Selected list ID not found" +msgstr "ID de lista seleccionado no encontado" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "No se ha podido crear campo personalizado" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "Campo seleccionado no encontrado" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "Configuración del campo actualizado" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "Configuración del campo no actualizado" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "Campo personalizado eliminado" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "No se pudo eliminar el campo especificado" + +#: routes/forms.js:78 +msgid "Could not create custom form" +msgstr "No se pudo crear el formulario personalizado" + +#: routes/forms.js:105 +msgid "Selected form not found" +msgstr "El formulario seleccionado no se encuentra" + +#: routes/forms.js:136 +msgid "The plaintext version for this email" +msgstr "Versión en formato texto para este correo electrónico" + +#: routes/forms.js:137 +msgid "Custom forms use MJML for formatting" +msgstr "Formularios personalizados utilizan MJML para dar formato" + +#: routes/forms.js:138 +msgid "See the MJML documentation here" +msgstr "" +"Consulte la documentación de MJML aquí " + +#: routes/forms.js:146 +msgid "Layout" +msgstr "Diseño" + +#: routes/forms.js:152 +msgid "Form Input Style" +msgstr "Estilo de entrada de Formulario" + +#: routes/forms.js:154 +msgid "" +"This CSS stylesheet defines the appearance of form input elements and alerts" +msgstr "" +"Esta hoja de estilo CSS define el aspecto de los elementos de entrada de " +"formulario y alertas" + +#: routes/forms.js:160 +msgid "Web - Subscribe" +msgstr "Web - Suscribirse" + +#: routes/forms.js:165 +msgid "Web - Confirm Subscription Notice" +msgstr "Web - Aviso Confirmar suscripción" + +#: routes/forms.js:170 +msgid "Mail - Confirm Subscription (MJML)" +msgstr "Mail - Confirmar suscripción (MJML)" + +#: routes/forms.js:175 +msgid "Mail - Confirm Subscription (Text)" +msgstr "Mail - Confirmar suscripción (texto)" + +#: routes/forms.js:180 +msgid "Mail - Already Subscribed (MJML)" +msgstr "Mail - Ya suscrito (MJML)" + +#: routes/forms.js:185 +msgid "Mail - Already Subscribed (Text)" +msgstr "Mail - Ya Suscrito (texto)" + +#: routes/forms.js:190 +msgid "Web - Subscribed Notice" +msgstr "Web - Aviso Suscripción" + +#: routes/forms.js:195 +msgid "Mail - Subscription Confirmed (MJML)" +msgstr "Mail - Suscripción confirmada (MJML)" + +#: routes/forms.js:200 +msgid "Mail - Subscription Confirmed (Text)" +msgstr "Mail - Suscripción confirmada (texto)" + +#: routes/forms.js:208 +msgid "Web - Manage Preferences" +msgstr "Web - Gestión de Preferencias" + +#: routes/forms.js:213 +msgid "Web - Manage Address" +msgstr "Web - Gestión de Dirección" + +#: routes/forms.js:218 +msgid "Web - Updated Notice" +msgstr "Web - Aviso actualizado" + +#: routes/forms.js:226 +msgid "Web - Unsubscribe" +msgstr "Web - Darse de baja" + +#: routes/forms.js:231 +msgid "Web - Confirm Unsubscription Notice" +msgstr "Web - Confirmar Aviso de baja" + +#: routes/forms.js:236 +msgid "Mail - Confirm Unsubscription (MJML)" +msgstr "Mail - Confirmar baja (MJML)" + +#: routes/forms.js:241 +msgid "Mail - Confirm Unsubscription (Text)" +msgstr "Mail - Confirmar baja (MJML)" + +#: routes/forms.js:246 +msgid "Mail - Confirm Address Change (MJML)" +msgstr "Mail - Confirmar Cambio de dirección (MJML)" + +#: routes/forms.js:251 +msgid "Mail - Confirm Address Change (Text)" +msgstr "Mail - Confirmar Cambio de dirección (texto)" + +#: routes/forms.js:256 +msgid "Web - Unsubscribed Notice" +msgstr "Web - Aviso Baja realizada" + +#: routes/forms.js:261 +msgid "Mail - Unsubscription Confirmed (MJML)" +msgstr "Mail - Baja Confirmada (MJML)" + +#: routes/forms.js:266 +msgid "Mail - Unsubscription Confirmed (Text)" +msgstr "Mail - Baja Confirmada (texto)" + +#: routes/forms.js:271 +msgid "Web - Manual Unsubscribe Notice" +msgstr "Web - Aviso de Manual de Baja" + +#: routes/forms.js:309 +msgid "Form settings updated" +msgstr "Configuración de formulario Actualizada" + +#: routes/forms.js:311 +msgid "Form settings not updated" +msgstr "Configuración de formulario No Actualizada" + +#: routes/forms.js:327 +msgid "Custom form deleted" +msgstr "Formulario Personalizado eliminado" + +#: routes/forms.js:329 +msgid "Could not delete specified form" +msgstr "No se ha podido eliminar Formulario especificado" + +#: routes/index.js:11 +msgid "Self Hosted Newsletter App" +msgstr "Auto alojamiento de Aplicación de Boletín " + +#: routes/links.js:39 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "" +"Vaya, no hemos podido encontrar un vínculo para la URL que ha hecho clic" + +#: routes/lists.js:82 +msgid "Could not create list" +msgstr "No se ha podido crear la lista" + +#: routes/lists.js:85 +msgid "List created" +msgstr "Lista creada" + +#: routes/lists.js:93 routes/lists.js:271 routes/lists.js:336 +#: routes/lists.js:375 routes/lists.js:444 routes/lists.js:469 +#: routes/lists.js:514 routes/lists.js:536 routes/lists.js:565 +#: routes/lists.js:644 routes/lists.js:701 routes/lists.js:728 +msgid "Could not find list with specified ID" +msgstr "No se ha podido encontrar la lista con el ID especificado" + +#: routes/lists.js:122 +msgid "List settings updated" +msgstr "Configuración de la lista actualizado" + +#: routes/lists.js:124 +msgid "List settings not updated" +msgstr "Configuración de la lista no actualizado" + +#: routes/lists.js:142 +msgid "List deleted" +msgstr "Lista eliminada" + +#: routes/lists.js:144 +msgid "Could not delete specified list" +msgstr "No se ha eliminado la lista especificada" + +#: routes/lists.js:206 +msgid "Unknown" +msgstr "desconocido" + +#: routes/lists.js:206 +msgid "Complained" +msgstr "Queja" + +#: routes/lists.js:237 +msgid "Invalid key" +msgstr "Clave inválida" + +#: routes/lists.js:239 +msgid "Expired key" +msgstr "Clave caducada" + +#: routes/lists.js:241 +msgid "Revoked key" +msgstr "Clave anulada" + +#: routes/lists.js:291 +msgid "Initializing" +msgstr "Iniciando" + +#: routes/lists.js:294 +msgid "Initialized" +msgstr "Iniciado" + +#: routes/lists.js:297 +msgid "Importing" +msgstr "Importando" + +#: routes/lists.js:303 +msgid "Errored" +msgstr "Erróneo" + +#: routes/lists.js:381 routes/lists.js:450 routes/lists.js:475 +msgid "Could not find subscriber with specified ID" +msgstr "No se pudo encontrar el Suscriptor con el especificado ID" + +#: routes/lists.js:427 +msgid "Could not add subscription" +msgstr "No se pudo añadir la suscripción" + +#: routes/lists.js:432 +msgid "%s was successfully added to your list" +msgstr "%s ha sido añadido a su lista con éxito" + +#: routes/lists.js:434 +msgid "%s was not added to your list" +msgstr "%s no ha sido añadido a su lista" + +#: routes/lists.js:456 +msgid "Could not unsubscribe user" +msgstr "No se ha dado de baja al usuario" + +#: routes/lists.js:459 +msgid "%s was successfully unsubscribed from your list" +msgstr "%s ha sido dado de baja de su lista con éxito" + +#: routes/lists.js:479 +msgid "%s was successfully removed from your list" +msgstr "%s ha sido eliminado de su lista con éxito" + +#: routes/lists.js:491 +msgid "Another subscriber with email address %s already exists" +msgstr "Otro suscriptor con correo electrónico %s ya existe" + +#: routes/lists.js:498 +msgid "Subscription settings updated" +msgstr "Configuración de suscripción actualizada" + +#: routes/lists.js:500 +msgid "Subscription settings not updated" +msgstr "Configuración de suscripción no actualizada" + +#: routes/lists.js:542 routes/lists.js:650 routes/lists.js:686 +#: routes/lists.js:714 routes/lists.js:734 +msgid "Could not find import data with specified ID" +msgstr "No se pudo encontrar datos de importación con especificado ID" + +#: routes/lists.js:573 +msgid "Could not process CSV" +msgstr "No se pudo procesar CSV" + +#: routes/lists.js:582 +msgid "Could not create importer" +msgstr "No se pudo crear importación" + +#: routes/lists.js:633 +msgid "Empty file" +msgstr "Archivo vacío" + +#: routes/lists.js:690 +msgid "Import started" +msgstr "Importación empezada" + +#: routes/lists.js:718 +msgid "Import restarted" +msgstr "Importación reiniciada" + +#: routes/lists.js:784 +msgid "One-step (i.e. no email with confirmation link)" +msgstr "Un paso (es decir, no email de confirmación)" + +#: routes/lists.js:790 +msgid "" +"One-step with unsubscription form (i.e. no email with confirmation link)" +msgstr "Un paso con formulario de baja (es decir, no email de confirmación)" + +#: routes/lists.js:796 +msgid "Two-step (i.e. an email with confirmation link will be sent)" +msgstr "Dos pasos (es decir, se envía email de confirmación)" + +#: routes/lists.js:802 +msgid "" +"Two-step with unsubscription form (i.e. an email with confirmation link will " +"be sent)" +msgstr "" +"Dos pasos con formulario de baja (es decir, se envía email de confirmación)" + +#: routes/lists.js:808 +msgid "" +"Manual (i.e. unsubscription has to be performed by the list administrator)" +msgstr "Manual (es decir, baja realizada por administrador)" + +#: routes/report-templates.js:246 +msgid "Could not create report template" +msgstr "No se pudo crear informe de plantillas" + +#: routes/report-templates.js:249 +msgid "Report template “%s” created" +msgstr "Informe de plantilla “%s” creado" + +#: routes/report-templates.js:257 +msgid "Could not find report template with specified ID" +msgstr "No se encuentra informe de plantilla con ID especificado" + +#: routes/report-templates.js:280 +msgid "Report template updated" +msgstr "Informe de plantilla actualizada" + +#: routes/report-templates.js:282 +msgid "Report template not updated" +msgstr "Informe de plantilla no actualizada" + +#: routes/report-templates.js:298 +msgid "Report template deleted" +msgstr "Informe de plantilla eliminada" + +#: routes/report-templates.js:300 +msgid "Could not delete specified report template" +msgstr "No se pudo eliminar Informe de plantilla especificado" + +#: routes/reports.js:124 routes/reports.js:130 +msgid "Could not create report" +msgstr "No se pudo crear informe" + +#: routes/reports.js:135 +msgid "Report “%s” created" +msgstr "Informe \"%s\" creado" + +#: routes/reports.js:146 routes/reports.js:224 routes/reports.js:239 +#: routes/reports.js:265 routes/reports.js:275 +msgid "Could not find report with specified ID" +msgstr "No se pudo encontrar el informe con el ID especificado" + +#: routes/reports.js:188 routes/reports.js:194 +msgid "Could not update report" +msgstr "No se pudo actualizar el informe" + +#: routes/reports.js:197 +msgid "Report updated" +msgstr "Informe actualizado" + +#: routes/reports.js:199 +msgid "Report not updated" +msgstr "Informe no actualizado" + +#: routes/reports.js:212 +msgid "Report deleted" +msgstr "Informe eliminado" + +#: routes/reports.js:214 +msgid "Could not delete specified report" +msgstr "No se pudo eliminar informe especificado" + +#: routes/reports.js:230 +msgid "Could not find report template" +msgstr "No se pudo encontrar informe especificado" + +#: routes/reports.js:260 +msgid "Unknown type of template" +msgstr "Tipo desconocido de la plantilla" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "No se pudo crear el segmento" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "Segmento creado" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "ID de segmento seleccionado no encontrado" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "Ajustes de segmento actualizados" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "Ajustes de segmento no actualizados" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "Segmento eliminado" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "No se pudo eliminar segmento especificado" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "No se pudo crear la regla" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "Regla creada" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "Ajustes de regla actualizados" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "Ajustes de regla no actualizados" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "Regla eliminada" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "No se pudo eliminar regla especificada" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "Usar TLS" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "Normalmente seleccionado para el puerto 465" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "Usar STARTTLS" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "Generalmente seleccionado por el puerto 587 y 25" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "No utilice el cifrado" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "Ajustes actualizados" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "Tipo de Correo Transporte no válido" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "Clave de acceso inválido" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "Credenciales AWS inválidas" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "Conexión rechazada, verificar nombre de host y puerto " + +#: routes/settings.js:195 +msgid "" +"Did not receive greeting message from server. This might happen when " +"connecting to a TLS port without using TLS." +msgstr "" +"No se recibió el mensaje de saludo de servidor. Esto puede ocurrir cuando se " +"conecta a un puerto TLS sin utilizar TLS." + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "No se recibió el mensaje de saludo de servidor." + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" +"Tiempo de conexión agotado. Comprobar la configuración del firewall, puerto " +"de destino probablemente está bloqueado." + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "Autenticación no aceptada, servidor STARTTLS espera a ser utilizados." + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "Error de autenticación, compruebe nombre de usuario y contraseña." + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "Verificación Remitente fallido." + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "Servidor respondió con: \"%s\"" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "Ajustes de remitente verificados, listo para enviar algún correo!" + +#: routes/subscription.js:33 +msgid "Not allowed by CORS" +msgstr "No es permitido por CORS" + +#: routes/subscription.js:61 routes/subscription.js:175 +#: routes/subscription.js:285 routes/subscription.js:381 +#: routes/subscription.js:458 routes/subscription.js:534 +#: routes/subscription.js:565 routes/subscription.js:625 +#: routes/subscription.js:681 routes/subscription.js:759 +#: routes/subscription.js:896 +msgid "Selected list not found" +msgstr "La lista seleccionada no se encuentra" + +#: routes/subscription.js:92 +msgid "Could not save subscription" +msgstr "No se pudo guardar la suscripción" + +#: routes/subscription.js:117 +msgid "Subscriber info corrupted or missing" +msgstr "Datos de Suscripción dañados o perdidos" + +#: routes/subscription.js:135 +msgid "Email address changed" +msgstr "Correo electrónico ha cambiado" + +#: routes/subscription.js:178 routes/subscription.js:384 +msgid "The list does not allow public subscriptions." +msgstr "La lista no permite suscripciones públicas." + +#: routes/subscription.js:354 routes/subscription.js:356 +msgid "Email address not set" +msgstr "Correo electrónico no establecido" + +#: routes/subscription.js:652 +msgid "" +"An email with further instructions has been sent to the provided address" +msgstr "" +"Un correo electrónico con más instrucciones ha sido enviado a la dirección " +"proporcionada" + +#: routes/subscription.js:860 routes/subscription.js:876 +msgid "Public key is not set" +msgstr "La clave pública no se ha establecido" + +#: routes/templates.js:84 +msgid "Could not create template" +msgstr "No se pudo crear la plantilla" + +#: routes/templates.js:87 +msgid "Template created" +msgstr "Planilla creada" + +#: routes/templates.js:126 +msgid "Template settings updated" +msgstr "Configuración de plantilla actualizada" + +#: routes/templates.js:128 +msgid "Template settings not updated" +msgstr "Configuración de plantilla no actualizada" + +#: routes/templates.js:144 +msgid "Template deleted" +msgstr "Plantilla eliminada" + +#: routes/templates.js:146 +msgid "Could not delete specified template" +msgstr "No se pudo eliminar la plantilla especificada" + +#: routes/triggers.js:62 routes/triggers.js:79 routes/triggers.js:154 +msgid "Could not find selected list" +msgstr "No se pudo encontrar lista seleccionada" + +#: routes/triggers.js:131 +msgid "Could not create trigger" +msgstr "No se pudo crear el Trigger" + +#: routes/triggers.js:138 +msgid "Trigger “%s” created" +msgstr "Trigger “%s” creado" + +#: routes/triggers.js:214 +msgid "Trigger settings updated" +msgstr "Ajustes de Trigger actualizados" + +#: routes/triggers.js:216 +msgid "Trigger settings not updated" +msgstr "Ajustes de Trigger no actualizados" + +#: routes/triggers.js:228 +msgid "Trigger deleted" +msgstr "Trigger eliminado" + +#: routes/triggers.js:230 +msgid "Could not delete specified trigger" +msgstr "No se pudo eliminar el Trigger especificado" + +#: routes/triggers.js:242 +msgid "Could not find trigger with specified ID" +msgstr "No se pudo encontrar el Trigger con el ID especificado" + +#: routes/triggers.js:255 +msgid "Trigger not found" +msgstr "Trigger no encontrado" + +#: routes/users.js:32 +msgid "" +"An email with password reset instructions has been sent to your email " +"address, if it exists on our system." +msgstr "" +"Un correo electrónico con instrucciones para restablecer la contraseña ha " +"sido enviada a su dirección de correo electrónico, si existe en nuestro " +"sistema." + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "Reinicio de Token desconocido o caducado" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "Tu contraseña ha sido cambiada exitosamente" + +#: routes/users.js:87 +msgid "User data not found" +msgstr "Datos de usuario no encontrados" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "Token de acceso actualizado" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "Token de acceso no actualizado" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "Información de cuenta actualizada" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "Información de cuenta no actualizada" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "Error de Feed: %s" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "Encontrado %s nueva campaña de mensajes desde Feed" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "No se ha encontrado nada nuevo en el Feed" + +#: services/feedcheck.js:146 +msgid "RSS entry %s" +msgstr "Entrada RSS %s" + +#: services/importer.js:249 +msgid "Could not access import file" +msgstr "No se pudo acceder al archivo de importación" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "Desconocido tipo de Trigger %s" From 1ff46768111e94a61dad24d0f2a7e56ba6234f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:14:54 -0500 Subject: [PATCH 14/26] Update Docker config template --- config/docker-production.toml.tmpl | 9 ++ config/docker.toml | 186 ----------------------------- 2 files changed, 9 insertions(+), 186 deletions(-) create mode 100644 config/docker-production.toml.tmpl delete mode 100644 config/docker.toml diff --git a/config/docker-production.toml.tmpl b/config/docker-production.toml.tmpl new file mode 100644 index 00000000..ca832181 --- /dev/null +++ b/config/docker-production.toml.tmpl @@ -0,0 +1,9 @@ +[mysql] +host="mysql" + +[redis] +enabled=true +host="redis" + +[reports] +enabled=true \ No newline at end of file diff --git a/config/docker.toml b/config/docker.toml deleted file mode 100644 index 38a76107..00000000 --- a/config/docker.toml +++ /dev/null @@ -1,186 +0,0 @@ -# This file is the default config file for Mailtrain. To use a environment specific -# configuration add new file {ENV}.{ext} (eg. "production.toml") to the same folder. -# {ENV} is defined by NODE_ENV environment variable. -# -# Do not modify this file directly, otherwise you might lose your modifications when upgrading -# -# You should only define the options you want to change in your additional config file. -# For example if the only thing you want to change is the port number for the www server -# then your additional config file should look like this: -# # production.toml -# [www] -# port=80 -# or if you want to use Javascript instead of TOML then the same file could look like this: -# // production.js -# module.exports = { -# www: { -# port: 80 -# } -# }; - -# Process title visible in monitoring logs and process listing -title="mailtrain" - -# Enabled HTML editors -editors=[ - ["summernote", "Summernote"], - ["grapejs", "GrapeJS"], - ["mosaico", "Mosaico"], - ["codeeditor", "Code Editor"] -] - -# Default language to use -language="en" - -# Inject custom styles in layout.hbs -# customstyles=["/custom/hello-world.css"] - -# Inject custom scripts in layout.hbs -# customscripts=["/custom/hello-world.js"] - -# Inject custom scripts in subscription/layout.mjml.hbs -# customsubscriptionscripts=["/custom/hello-world.js"] - -# If you start out as a root user (eg. if you want to use ports lower than 1000) -# then you can downgrade the user once all services are up and running -#user="mailtrain" -#group="mailtrain" - -# If Mailtrain is started as root, "Reports" feature drops the privileges of script generating the report to disallow -# any modifications of Mailtrain code and even prohibits reading the production configuration (which contains the MySQL -# password for read/write operations). The rouser/rogroup determines the user to be used -#rouser="nobody" -#rogroup="nogroup" - -[log] -# silly|verbose|info|http|warn|error|silent -level="verbose" - -[www] -# HTTP port to listen on -port=3000 -# HTTP interface to listen on -host="0.0.0.0" -# Secret for signing the session ID cookie -secret="a cat" -# Session length in seconds when "remember me" is checked -remember=2592000 # 30 days -# logger interface for expressjs morgan -log="dev" -# Is the server behind a proxy? true/false -# Set this to true if you are serving Mailtrain as a virtual domain through Nginx or Apache -proxy=false -# maximum POST body size -postsize="2MB" -# Uncomment to set uploads folder location for temporary data. Defaults to os.tmpdir() -# If the service is started by `npm start` then os.tmpdir() points to CWD -#tmpdir="/tmp" - -[mysql] -host="mysql" -user="mailtrain" -password="mailtrain" -database="mailtrain" -# Some installations, eg. MAMP can use a different port (8889) -# MAMP users should also turn on "Allow network access to MySQL" otherwise MySQL might not be accessible -port=3306 -charset="utf8mb4" -timezone="local" - -[redis] -# enable to use Redis session cache or disable if Redis is not installed -enabled=true -host="redis" -port=6379 -db=5 -# Uncomment if your Redis installation requires a password -#password="" - -[verp] -# Enable to start an MX server that detects bounced messages using VERP -# In most cases you do not want to use it -# Requires root privileges -enabled=false -port=2525 -host="0.0.0.0" -# With DMARC, the Return-Path and From address must match the same domain. -# By default we get around this by using the VERP address in the Sender header, -# with the side effect that some email clients diplay an ugly "on behalf of" message. -# You can safely disable this Sender header if you're not using DMARC or your -# VERP hostname is in the same domain as the From address. -# disablesenderheader=true - -[ldap] -# enable to use ldap user backend -enabled=false -host="localhost" -port=3002 -baseDN="ou=users,dc=company" -filter="(|(username={{username}})(mail={{username}}))" -#Username field in LDAP (uid/cn/username) -uidTag="username" -passwordresetlink="" - -[postfixbounce] -# Enable to allow writing Postfix bounce log to Mailtrain listener -# If enabled, tail mail.log to Mailtrain with the following command: -# tail -f -n +0 /var/log/mail.log | nc localhost 5699 - -enabled=false -port=5699 -# allow connections from localhost only -host="127.0.0.1" - -# extra options for nodemailer -[nodemailer] -#textEncoding="base64" - -[queue] -# How many parallel sender processes to spawn -# You can use more than 1 process only if you have Redis enabled -processes=1 - -[cors] -# Allow subscription widgets to be embedded -# origins=['https://www.example.com'] - -[mosaico] -# Installed templates -templates=[["versafix-1", "Versafix One"]] -# Inject custom scripts -# customscripts=["/mosaico/custom/my-mosaico-plugin.js"] - -[grapejs] -# Installed templates -templates=[ - ["demo", "HTML Template"], - ["aves", "MJML Template"] -] - -[reports] -# The whole reporting functionality can be disabled below if the they are not needed and the DB cannot be -# properly protected. -# Reports rely on custom user defined Javascript snippets defined in the report template. The snippets are run on the -# server when generating a report. As these snippets are stored in the DB, they pose a security risk because they can -# help gaining access to the server if the DB cannot -# be properly protected (e.g. if it is shared with another application with security weaknesses). -# Mailtrain mitigates this problem by running the custom Javascript snippets in a chrooted environment and under a -# DB user that cannot modify the database (see userRO in [mysql] above). However the chrooted environment is available -# only if Mailtrain is started as root. The chrooted environment still does not prevent the custom JS script in -# performing network operations and in generating XSS attacks as part of the report. -# The bottom line is that if people who are creating report templates or have write access to the DB cannot be trusted, -# then it's safer to switch off the reporting functionality below. -enabled=false - -[testserver] -# Starts a vanity server that redirects all mail to /dev/null -# Mostly needed for local development -enabled=false -port=5587 -mailboxserverport=3001 -host="0.0.0.0" -username="testuser" -password="testpass" -logger=false - -[seleniumwebdriver] -browser="phantomjs" From f142175917fcc0954de100838f9dd44eecf984f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:15:52 -0500 Subject: [PATCH 15/26] Add entrypoint that copies production configs --- docker-entrypoint.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker-entrypoint.sh diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 00000000..e6df2530 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +if [ ! -f "/app/config/production.toml" ] ; then + echo "No production.toml, copying from docker-production.toml.tmpl" + cp /app/config/docker-production.toml.tmpl /app/config/production.toml +fi +if [ ! -f "/app/workers/reports/config/production.toml" ] ; then + echo "No production.toml for reports, copying from docker-production.toml.tmpl" + cp /app/config/docker-production.toml.tmpl /app/workers/reports/config/production.toml +fi +exec "$@" \ No newline at end of file From 54f65c506e491ca9cd1ef8a29e7b949a84fcf3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:17:00 -0500 Subject: [PATCH 16/26] Update Dockerfile, order installs, NODE_ENV, add entrypoint --- Dockerfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0f759e57..6d2c3228 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,13 @@ FROM node:8.1 -COPY . /app +# First install dependencies +COPY ./package.json ./app/ WORKDIR /app/ -ENV NODE_ENV docker +ENV NODE_ENV production RUN npm install --no-progress --production && npm install --no-progress passport-ldapjs +# Later, copy the app files. That improves development speed as buiding the Docker image will not have +# to download and install all the NPM dependencies every time there's a change in the source code +COPY . /app EXPOSE 3000 +ENTRYPOINT ["/app/docker-entrypoint.sh"] CMD ["node", "index.js"] \ No newline at end of file From 31565f4cab6c84ad1fc52567cd0588b8873d26fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:17:34 -0500 Subject: [PATCH 17/26] Add node_modules to .dockerignore to avoid unneeded building time / overhead --- .dockerignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..b512c09d --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file From fb6b98a6184ab9719543fdf9f2e39defc61be0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:20:15 -0500 Subject: [PATCH 18/26] Make docker-compose.override.yml a template --- docker-compose.override.yml => docker-compose.override.yml.tmpl | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker-compose.override.yml => docker-compose.override.yml.tmpl (100%) diff --git a/docker-compose.override.yml b/docker-compose.override.yml.tmpl similarity index 100% rename from docker-compose.override.yml rename to docker-compose.override.yml.tmpl From 5229f6edaf99e974c35ef0f41340b1a324d14240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:22:38 -0500 Subject: [PATCH 19/26] Add volume for reports --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index fe82724f..ba00757e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,8 +22,10 @@ services: - mailtrain-node-config:/app/config - mailtrain-node-data:/app/public/grapejs/uploads - mailtrain-node-data:/app/public/mosaico/uploads + - mailtrain-node-reports:/app/protected/reports volumes: mailtrain-mysq-data: {} mailtrain-redis-data: {} mailtrain-node-data: {} mailtrain-node-config: {} + mailtrain-node-reports: {} From fb3d37cfc0cf8ed8382ec7e0f8ba1081a5322b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:31:57 -0500 Subject: [PATCH 20/26] Update README --- README.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7018101c..60726293 100644 --- a/README.md +++ b/README.md @@ -120,20 +120,25 @@ If you are using the bundled ZoneMTA then you should make sure you are using a p With proper SPF, DKIM and PTR records (DMARC wouldn't hurt either) I got perfect 10/10 score out from [MailTester](https://www.mail-tester.com/) when sending a campaign message to a MailTester test address. I did not have VERP turned on, so the sender address matched return path address. ### Simple Install (Docker) -##### Requirements: - * [Docker](https://www.docker.com/) - * [Docker Compose](https://docs.docker.com/compose/) +#### Requirements: - * Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain` - * **Note**: depending on how you have configured your system and Docker you may need to prepend the commands below with `sudo`. - * Bring up the stack with: `docker-compose up -d`, by default it will use the included `docker-compose.yml` file and override some configurations taken from the `docker-compose.override.yml` file. - * You might want to modify the `docker-compose.yml` or `docker-compose.override.yml` file, modify port mappings, change volume paths, etc. - * If you want to use only / copy the `docker-compose.yml` file (for example, if you were deploying with Rancher), you may need to first run `docker-compose build` to make sure your system has a Docker image `mailtrain:latest`. - * Open [http://localhost:3000/](http://localhost:3000/) (change the host name `localhost` to the name of the host where you are deploying the system). - * Authenticate as `admin`:`test` - * Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration. - * Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password. + * [Docker](https://www.docker.com/) + * [Docker Compose](https://docs.docker.com/compose/) +#### Install: + +* Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain` +* **Note**: depending on how you have configured your system and Docker you may need to prepend the commands below with `sudo`. +* Copy the file `docker-compose.override.yml.tmpl` to `docker-compose.override.yml.tmpl` and modify it if you need to. +* Bring up the stack with: `docker-compose up -d`, by default it will use the included `docker-compose.yml` file and override some configurations taken from the `docker-compose.override.yml` file. +* If you want to use only / copy the `docker-compose.yml` file (for example, if you were deploying with Rancher), you may need to first run `docker-compose build` to make sure your system has a Docker image `mailtrain:latest`. +* Open [http://localhost:3000/](http://localhost:3000/) (change the host name `localhost` to the name of the host where you are deploying the system). +* Authenticate as user `admin` with password `test` +* Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration. +* Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password. + +**Note**: If you need to add or modify custom configurations, copy the file `config/docker-production.toml.tmpl` to `config/production.toml` and modify as you need. By default, the Docker image will do just that, automatically, so you can bring up the stack and it will work with default configurations. + ### Manual Install (any OS that supports Node.js) From 58f607ffc4bdfd52b486be09a98b052bcf089ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:32:29 -0500 Subject: [PATCH 21/26] Add to .gitignore files that should not be commited --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 590e00cc..9ab19ae8 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ public/grapejs/uploads/* public/grapejs/templates/* !public/grapejs/templates/demo !public/grapejs/templates/aves + +config/production.toml +workers/reports/config/production.toml +docker-compose.override.yml \ No newline at end of file From 8d56f0763e776074dc6680876386071594c2a90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 18:49:35 -0500 Subject: [PATCH 22/26] Use production.toml for reports when it exists in config --- docker-entrypoint.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index e6df2530..00101596 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -6,7 +6,13 @@ if [ ! -f "/app/config/production.toml" ] ; then cp /app/config/docker-production.toml.tmpl /app/config/production.toml fi if [ ! -f "/app/workers/reports/config/production.toml" ] ; then - echo "No production.toml for reports, copying from docker-production.toml.tmpl" - cp /app/config/docker-production.toml.tmpl /app/workers/reports/config/production.toml + echo "No production.toml for reports" + if [ -f "/app/config/production.toml" ] ; then + echo "copying config/production.toml to reports config directory" + cp /app/config/production.toml /app/workers/reports/config/production.toml + else + echo "copying config/docker-production.toml.tmpl to reports config directory as production.toml" + cp /app/config/docker-production.toml.tmpl /app/workers/reports/config/production.toml + fi fi exec "$@" \ No newline at end of file From ead3dbbfb353ebb44d7518d1c27c496537344ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Jun 2017 19:40:43 -0500 Subject: [PATCH 23/26] Update Dockerfile to avoid file system permission issues --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6d2c3228..80ae090c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,5 +9,5 @@ RUN npm install --no-progress --production && npm install --no-progress passport # to download and install all the NPM dependencies every time there's a change in the source code COPY . /app EXPOSE 3000 -ENTRYPOINT ["/app/docker-entrypoint.sh"] +ENTRYPOINT ["bash", "/app/docker-entrypoint.sh"] CMD ["node", "index.js"] \ No newline at end of file From f0e32ef5a56a70d73208fc26848c8cefef08fcbb Mon Sep 17 00:00:00 2001 From: witzig Date: Sat, 1 Jul 2017 01:50:50 +0200 Subject: [PATCH 24/26] Added note about blacklist, #272 --- views/blacklist.hbs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/views/blacklist.hbs b/views/blacklist.hbs index 4a1fddca..48ceb332 100644 --- a/views/blacklist.hbs +++ b/views/blacklist.hbs @@ -8,10 +8,13 @@

-
- +
+
+ +
+
- +

A blacklisted email address will not receive messages from any campaign. The blacklist does not apply to transactional messages.

From 191dc7b54157ad60e34aa9cbeb17ac08b1a527ba Mon Sep 17 00:00:00 2001 From: Bruce Mackintosh Date: Mon, 3 Jul 2017 19:44:45 +0100 Subject: [PATCH 25/26] Changed the trigger `treshold` value from 6 hours to 24 hours, so that dates stored via the API as 00:00:00 are correctly picked up and added to the queue. --- lib/models/triggers.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/models/triggers.js b/lib/models/triggers.js index 35e424d3..6d2e49c6 100644 --- a/lib/models/triggers.js +++ b/lib/models/triggers.js @@ -98,7 +98,10 @@ module.exports.getQuery = (id, callback) => { } let limit = 300; - let treshold = 3600 * 6; // time..NOW..time+6h, 6 hour window after trigger target to detect it + + // time..NOW..time + 24h, 24 hour window after trigger target to detect it + //We need a 24 hour window for triggers as the format for dates added via the API are stored as 00:00:00 + let treshold = 3600 * 24; let intervalQuery = (column, seconds, treshold) => column + ' <= NOW() - INTERVAL ' + seconds + ' SECOND AND ' + column + ' >= NOW() - INTERVAL ' + (treshold + seconds) + ' SECOND'; From bd570000e11d340e957bcd52b009d7d4d3935f54 Mon Sep 17 00:00:00 2001 From: witzig Date: Thu, 6 Jul 2017 15:03:04 +0200 Subject: [PATCH 26/26] Added documentation for API endpoint /api/field/:listId This has accidentally been removed with PR #203. --- views/users/api.hbs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/views/users/api.hbs b/views/users/api.hbs index 1a322032..5fed44e0 100644 --- a/views/users/api.hbs +++ b/views/users/api.hbs @@ -142,6 +142,45 @@
curl -XPOST {{serviceUrl}}api/delete/B16uVTdW?access_token={{accessToken}} \
 --data 'EMAIL=test@example.com'
+

POST /api/field/:listId – {{#translate}}Add new custom field{{/translate}}

+ +

+ {{#translate}}This API call creates a new custom field for a list.{{/translate}} +

+ +

+ GET {{#translate}}arguments{{/translate}} +

+
    +
  • access_token – {{#translate}}your personal access token{{/translate}} +
+ +

+ POST {{#translate}}arguments{{/translate}} +

+
    +
  • NAME – {{#translate}}field name{{/translate}} ({{#translate}}required{{/translate}})
  • +
  • TYPE – {{#translate}}one of the following types:{{/translate}} +
      + {{#each allowedTypes}} +
    • + {{type}} {{description}} +
    • + {{/each}} +
    +
  • +
  • GROUP – {{#translate}}If the type is 'option' then you also need to specify the parent element ID{{/translate}}
  • +
  • GROUP_TEMPLATE – {{#translate}}Template for the group element. If not set, then values of the elements are joined with commas{{/translate}}
  • +
  • VISIBLE – yes/no, {{#translate}}if not visible then the subscriber can not view or modify this value at the profile page{{/translate}}
  • +
+ +

+ {{#translate}}Example{{/translate}} +

+ +
curl -XPOST {{serviceUrl}}api/field/B16uVTdW?access_token={{accessToken}} \
+--data 'NAME=Birthday&TYPE=birthday-us&VISIBLE=yes'
+

GET /api/blacklist/get – {{#translate}}Get list of blacklisted emails{{/translate}}