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 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..91926fcd --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "nodemailer" +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 04f79c0e..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; - -module.exports = { - rules: { - indent: [2, 4, { - SwitchCase: 1 - }], - quotes: [2, 'single'], - 'linebreak-style': [2, 'unix'], - semi: [2, 'always'], - strict: [2, 'global'], - eqeqeq: 2, - 'dot-notation': 2, - curly: 2, - 'no-fallthrough': 2, - 'quote-props': [2, 'as-needed'], - 'no-unused-expressions': [2, { - allowShortCircuit: true - }], - 'no-unused-vars': 2, - 'no-undefined': 2, - 'handle-callback-err': 2, - 'no-new': 2, - 'new-cap': 2, - 'no-eval': 2, - 'no-invalid-this': 2, - radix: [2, 'always'], - 'no-use-before-define': [2, 'nofunc'], - 'callback-return': [2, ['callback', 'cb', 'done']], - 'comma-dangle': [2, 'never'], - 'comma-style': [2, 'last'], - 'no-regex-spaces': 2, - 'no-empty': 2, - 'no-duplicate-case': 2, - 'no-empty-character-class': 2, - 'no-redeclare': [2, { - builtinGlobals: true - }], - 'block-scoped-var': 2, - 'no-sequences': 2, - 'no-throw-literal': 2, - 'no-useless-call': 2, - 'no-useless-concat': 2, - 'no-void': 2, - yoda: 2, - 'no-undef': 2, - 'global-require': 2, - 'no-var': 2, - 'no-bitwise': 2, - 'no-lonely-if': 2, - 'no-mixed-spaces-and-tabs': 2, - 'arrow-body-style': [2, 'as-needed'], - 'arrow-parens': [2, 'as-needed'], - 'prefer-arrow-callback': 2, - 'object-shorthand': 2, - 'prefer-spread': 2 - }, - env: { - es6: true, - node: true - }, - extends: 'eslint:recommended', - globals: { - it: true, - describe: true, - beforeEach: true, - afterEach: true - }, - fix: true -}; diff --git a/.gitignore b/.gitignore index 8b8d217b..dbd42224 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,34 @@ +/.idea +/last-failed-e2e-test.* + node_modules npm-debug.log +package-lock.json .DS_Store -development.toml +config/development* +config/production* +config/test* +workers/reports/config/development.* +workers/reports/config/production.* +workers/reports/config/test.* dump.rdb + +# generate POT file every time you want to update your PO file +languages/mailtrain.pot + +public/mosaico/uploads/* +!public/mosaico/uploads/README.md +public/mosaico/custom/* +!public/mosaico/custom/README.md +public/mosaico/templates/* +!public/mosaico/templates/versafix-1 + +public/grapejs/uploads/* +!public/grapejs/uploads/README.md +public/grapejs/templates/* +!public/grapejs/templates/demo +!public/grapejs/templates/aves + +config/production.toml +workers/reports/config/production.toml +docker-compose.override.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..dc5ea4b5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +dist: trusty +sudo: required +language: node_js +node_js: + - 8 +services: + - mysql +before_install: + - sudo apt-get -q -y install pwgen imagemagick +install: + - sudo bash test/e2e/install.sh + - npm install +before_script: + - npm run starttest > /dev/null 2>&1 & + - sleep 10 +script: + - grunt + - npm run _e2e diff --git a/BACKERS.md b/BACKERS.md new file mode 100644 index 00000000..6439658d --- /dev/null +++ b/BACKERS.md @@ -0,0 +1,18 @@ +# Crowdfunding Backers + +Mailtrain received funding from a [crowdfunding campaign](https://www.indiegogo.com/at/mailtrain/8720095). This was to enable me to spend the time required to get automation support into Mailtrain. These are the people who contributed to this fund raiser. + + * iRedMail - free, open source mail server solution <[www.iredmail.org](http://www.iredmail.org/)> + * Richard Adleta + * Wes Bos + * Christophe Lombart + * Anselm Hannemann + * Jens Carroll + * Anonymous + * Brett Nelson + * Jason Pelker + * Leif Singer + * Eve Land + * Diana Espino + * Moussa Clarke + * Carl Hauschke diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..7b18d957 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,149 @@ +# Changelog + +## 1.23.2 2017-04-04 + + * Allow skipping DNS check for imports + * Added option to use subscription widgets + +## 1.23.0 2017-03-19 + + * Fixed security issue where description tags were able to include script tags. Reported by Andreas Lindh. Fixed with [ae6affda](https://github.com/Mailtrain-org/mailtrain/commit/ae6affda8193f034e06f7e095ee23821a83d5190) + * Fixed security issue where templates that looked like file paths loaded content from arbitrary files. Reported by Andreas Lindh. Fixed with [0879fa41](https://github.com/Mailtrain-org/mailtrain/commit/0879fa412a2d4a417aeca5cd5092a8f86531e7ef) + * Fixed security issue where users were able to use html tags in subscription values. Reported by Andreas Lindh. Fixed with [9d5fb816](https://github.com/Mailtrain-org/mailtrain/commit/9d5fb816c937114966d4f589e1ad4e164ff3a187) + * Support for multiple HTML editors (Mosaico, GrapeJS, Summernote, HTML code) + +## 1.22.0 2017-03-02 + + * Reverted license back to GPL-v3 to support Mosaico + +## 1.21.0 2017-02-17 + + * Changed license from MIT to EUPL-1.1 + * Added support for sending mail using AWS SES + +## 1.20.0 2016-12-11 + + * Added option to distribute sending queue between multiple processes to speed up delivery + +## 1.19.0 2016-09-15 + + * Changed license from GPL-V3 to MIT + +## 1.18.0 2016-09-08 + + * Updated installation script to bundle ZoneMTA as the default sending engine + * Added new option to disable clicked and opened tracking + * Store remote IP for subscription confirmations + +## 1.17.0 2016-08-29 + + * Added new custom field for JSON data that is rendered using Handlebars when included in an email + +## 1.16.0 2016-08-29 + + * Render list values using Handlebars templates + * Added new API method to create custom fields + * Added LDAP authentication support + +## 1.15.0 2016-07-28 + + * Check SMTP settings using AJAX instead of posting entire form + +## 1.14.0 2016-07-09 + + * Fixed ANY match segments with range queries + * Added an option to disable un/subscribe confirmation messages + * Added support for throttling when sending messages + * Added preview links in message lists + +## 1.13.0 2016-06-23 + + * Added API method to delete subscribers + * Added a counter to triggers with a view to list all subscribers that caused this trigger to fire + +## 1.12.1 2016-06-23 + + * Fixed invalid base SQL dump + +## 1.12.0 2016-06-22 + + * Automation support. Create triggers that send a campaign once fired + * Fixed an issue with unresolved URL redirects + * Added support for relative date ranges in segments + +## 1.11.0 2016-05-31 + + * Retry transactional mail if failed with soft error (4xx) + * New feature to preview campaigns using selected test users + +## 1.10.1 2016-05-26 + + * Fix a bug with SMTP transport instance where campaign sending stalled until server was restarted + +## 1.10.0 2016-05-25 + + * Fetch multiple unsent messages at once to speed up delivery + * Fixed a bug of counting unsubscribers correctly + * Use LONGTEXT for template text fields (messages might include inlined images which are larger than 64kB) + +## 1.9.0 2016-05-16 + + * New look + * Added views for bounced/unsubscribed/complained etc. + +## 1.8.2 2016-05-13 + + * Added missing views for subscribers who clicked on any link and subscribers who opened the message + +## 1.8.1 2016-05-13 + + * Fixed an issue in API + +## 1.8.0 2016-05-13 + + * Show details about subscribers who clicked on a specific link + +## 1.7.0 2016-05-11 + + * Updated API, added new option **REQUIRE_CONFIRMATION** for subscriptions to send confirmation email before subscribing + +## 1.6.0 2016-05-07 + + * Added simple API support for adding and removing list subscriptions + +## 1.5.0 2016-05-05 + + * Fixed a bug in unsubscribing through the admin interface + * Added individual link click stats + +## 1.4.1 2016-05-04 + + * Added support for RSS templates + +## 1.4.0 2016-05-04 + + * Added support for RSS campaigns + * Subscribers get timezone attached to their profile + * Outgoing messages are preprocessed using juice + * Added installation script for easier setup + +## 1.3.0 2016-04-29 + + * Added option to use an URL as message source (when message needs to be rendered a POST request with Merge Tags as the POST body is made against that URL) + * Added option to schedule sending. You can set optional delay time when starting campaign sending. Once this time is reached sending starts automatically + * Show meaningful MySQL error when connection fails + +## 1.2.0 2016-04-25 + + * Rewrite merge tags in links (allows using links like `http://example.com/?u=[FIRST_NAME]` in messages) + * Added view for Imports to list failed addresses + * Automatic SQL table creation on initial run (no need for the `mysql` command anymore) + * Automatic SQL table updates on startup + * Send welcome and unsubscribe confirmation emails for subscribers + * Added support for GPG encryption for outgoing messages (requires custom field "GPG Key" set up for the list) + * Added new SMTP option: allow self-signed certs + * Added new setting: Disable WYSIWG editor (allows better handling of complex HTML templates) + * Allow downgrading user when server started as root (user is downgraded once all ports are bound) + * Added Nitrous.io one-click install button for easy try-out + * Added Max Post Size option to allow larger payloads from bounce webhooks + * Added VERP support to catch bounces using built in VERP smtp-server (disabled by default) diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index d5a6c86b..00000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog - -## 1.3.0 2016-04-29 - - * Added option to use an URL as message source (when message needs to be rendered a POST request with Merge Tags as the POST body is made against that URL) - * Added option to schedule sending. You can set optional delay time when starting campaign sending. Once this time is reached sending starts automatically - * Show meaningful MySQL error when connection fails - -## 1.2.0 2016-04-25 - - * Rewrite merge tags in links (allows using links like `http://example.com/?u=[FIRST_NAME]` in messages) - * Added view for Imports to list failed addresses - * Automatic SQL table creation on initial run (no need for the `mysql` command anymore) - * Automatic SQL table updates on startup - * Send welcome and unsubscribe confirmation emails for subscribers - * Added support for GPG encryption for outgoing messages (requires custom field "GPG Key" set up for the list) - * Added new SMTP option: allow self-signed certs - * Added new setting: Disable WYSIWG editor (allows better handling of complex HTML templates) - * Allow downgrading user when server started as root (user is downgraded once all ports are bound) - * Added Nitrous.io one-click install button for easy try-out - * Added Max Post Size option to allow larger payloads from bounce webhooks - * Added VERP support to catch bounces using built in VERP smtp-server (disabled by default) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..fcbd6fd0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +# Mutistaged Node.js Build +FROM node:10-alpine as builder + +# Install system dependencies +RUN set -ex; \ + apk add --update --no-cache \ + make gcc g++ git python + +# Copy package.json dependencies +COPY server/package.json /app/server/package.json +COPY server/package-lock.json /app/server/package-lock.json +COPY client/package.json /app/client/package.json +COPY client/package-lock.json /app/client/package-lock.json +COPY shared/package.json /app/shared/package.json +COPY shared/package-lock.json /app/shared/package-lock.json +COPY zone-mta/package.json /app/zone-mta/package.json +COPY zone-mta/package-lock.json /app/zone-mta/package-lock.json + +# Install dependencies in each directory +RUN cd /app/client && npm install +RUN cd /app/shared && npm install --production +RUN cd /app/server && npm install --production +RUN cd /app/zone-mta && npm install --production + +# 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 + +RUN set -ex; \ + cd /app/client && \ + npm run build && \ + rm -rf node_modules + +# Final Image +FROM node:10-alpine + +WORKDIR /app/ + +# Install system dependencies +RUN set -ex; \ + apk add --update --no-cache \ + pwgen netcat-openbsd bash imagemagick + +COPY --from=builder /app/ /app/ + +EXPOSE 3000 3003 3004 +ENTRYPOINT ["bash", "/app/docker-entrypoint.sh"] \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 9fabf01d..797686e6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -5,18 +5,32 @@ module.exports = function (grunt) { // Project configuration. grunt.initConfig({ eslint: { - all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'Gruntfile.js', 'app.js', 'index.js', '.eslintrc.js'] + all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'services/**/*.js', 'Gruntfile.js', 'app.js', 'index.js', 'routes/editorapi.js'] }, nodeunit: { - all: ['test/**/*-test.js'] + all: ['test/nodeunit/**/*-test.js'] + }, + + jsxgettext: { + test: { + files: [{ + src: ['views/**/*.hbs', 'lib/**/*.js', 'routes/**/*.js', 'services/**/*.js', 'app.js', 'index.js', '!ignored'], + output: 'mailtrain.pot', + 'output-dir': './languages/' + }], + options: { + keyword: ['translate', '_'] + } + } } }); // Load the plugin(s) grunt.loadNpmTasks('grunt-eslint'); grunt.loadNpmTasks('grunt-contrib-nodeunit'); + grunt.task.loadTasks('tasks'); // Tasks - grunt.registerTask('default', ['eslint', 'nodeunit']); + grunt.registerTask('default', ['eslint', 'nodeunit', 'jsxgettext']); }; diff --git a/LICENSE b/LICENSE index ded1b7be..00223092 100644 --- a/LICENSE +++ b/LICENSE @@ -1,15 +1,681 @@ -Mailtrain, self hosted email newsletter app -Copyright (C) 2016 Andris Reinman + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + + + ADDITIONAL LICENSES + +This product may include third party code/libraries, see NOTICE.txt for details +on their licensing and copyright. -You should have received a copy of the GNU General Public License -along with this program. If not, see . diff --git a/README.md b/README.md index 738262d1..aed0e107 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,64 @@ +## Mailtrain v2 beta is available. Check it out here https://github.com/Mailtrain-org/mailtrain/tree/development + # Mailtrain -[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v5+) and MySQL (v5.5+ or MariaDB). +[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB). ![](http://mailtrain.org/mailtrain.png) ## Features -Mailtrain supports subscriber list management, list segmentation, custom fields, email templates, large CSV list import files, etc. +* Subscriber list management +* List segmentation +* Custom fields +* Email templates +* Large CSV list import files -Subscribe to Mailtrain Newsletter [here](http://mailtrain.org/subscription/EysIv8sAx) (uses Mailtrain obviously) +Subscribe to Mailtrain Newsletter [here](https://mailtrain.org/subscription/S18sew2wM) (uses Mailtrain obviously) -## Cons +## Hardware Requirements +* 1 vCPU +* 1024 MB RAM - * Alpha-grade software. Might or might not work as expected - * Awful code base, needs refactoring - * No tests - * No documentation +## Quick Start - Deploy with Docker +#### Requirements: -## Requirements + * [Docker](https://www.docker.com/) + * [Docker Compose](https://docs.docker.com/compose/) - * Nodejs v5+ - * MySQL v5.5 or MariaDB - * Redis (optional, used for session storage only) +#### Steps: +Depending on how you have configured your system and Docker you may need to prepend the commands below with `sudo`. -## Installation +* 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` +* Copy the file `docker-compose.override.yml.tmpl` to `docker-compose.override.yml` and modify it if you need to. +* Bring up the stack with: `docker-compose up -d` +* Start: `docker-compose start` +* 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, especially replace the value in `Service Address (URL)` from `localhost` to the actual IP or domain if installed on a server, otherwise e.g. image upload will not work. +* Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password. - 1. Download Mailtrain files using git: `git clone git://github.com/andris9/mailtrain.git` and open Mailtrain folder `cd mailtrain` - 2. Run `npm install` in the Mailtrain folder to install required dependencies +## Quick Start - Manual Install (any OS that supports Node.js) + +### Requirements: + * Mailtrain requires at least **Node.js v7**. If you want to use an older version of Node.js then you should use version v1.24 of Mailtrain. You can either download it [here](https://github.com/Mailtrain-org/mailtrain/archive/v1.24.0.zip) or if using git then run `git checkout v1.24.0` before starting it + + 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 `npm install --production` in the Mailtrain folder to install required dependencies 3. Copy [config/default.toml](config/default.toml) as `config/production.toml` and update MySQL and any other settings in it 4. Run the server `NODE_ENV=production npm start` 5. Open [http://localhost:3000/](http://localhost:3000/) 6. Authenticate as `admin`:`test` - 7. Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration + 7. Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration, especially replace the value in `Service Address (URL)` from `localhost` to the actual IP or domain if installed on a server, otherwise e.g. image upload will not work. 8. Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password -## Upgrade +## Read The Docs +For more information, please [read the docs](http://docs.mailtrain.org/). - * Replace old files with new ones by running in the Mailtrain folder `git pull origin master` - * Run `npm install --production` in the Mailtrain folder - -## Using environment variables - -Some servers expose custom port and hostname options through environment variables. To support these, create a new configuration file `config/local.js`: - -``` -module.exports = { - www: { - port: process.env.OPENSHIFT_NODEJS_PORT, - host: process.env.OPENSHIFT_NODEJS_IP - } -}; -``` - -Mailtrain uses [node-config](https://github.com/lorenwest/node-config) for configuration management and thus the config files are loaded in the following order: - - 1. default.toml - 2. {NODE_ENV}.toml (eg. development.toml or production.toml) - 3. local.js - -### Running behind Nginx proxy - -Edit [mailtrain.nginx](setup/mailtrain.nginx) (update `server_name` directive) and copy it to `/etc/nginx/sites-enabled` - -### Running as an Upstart service in Ubuntu 14.04 - -Edit [mailtrain.conf](setup/mailtrain.conf) (update application folder) and copy it to `/etc/init` - -## Nitrous Quickstart - -You can quickly create a free development environment for this Mailtrain project in the cloud on www.nitrous.io: - - - Nitrous Quickstart - - -In the IDE, start Mailtrain via `Run > Start Mailtrain` and access your site via `Preview > 3000`. - -## Bounce handling - -Mailtrain uses webhooks integration to detect bounces and spam complaints. Currently supported webhooks are: - - * **AWS SES** – create a SNS topic for complaints and bounces and use `http://domain/webhooks/aws` as the subscriber URL for these topics - * **SparkPost** – use `http://domain/webhooks/sparkpost` as the webhook URL for bounces and complaints ([instructions](https://github.com/andris9/mailtrain/wiki/Setting-up-Webhooks-for-SparkPost)) - * **SendGrid** – use `http://domain/webhooks/sendgrid` as the webhook URL for bounces and complaints ([instructions](https://github.com/andris9/mailtrain/wiki/Setting-up-Webhooks-for-SendGrid)) - * **Mailgun** – use `http://domain/webhooks/mailgun` as the webhook URL for bounces and complaints ([instructions](https://github.com/andris9/mailtrain/wiki/Setting-up-Webhooks-for-Mailgun)) - -Additionally Mailtrain (v1.1+) is able to use VERP-based bounce handling. This would require to have a compatible SMTP relay (the services mentioned above strip out or block VERP addresses in the SMTP envelope) and you also need to set up special MX DNS name that points to your Mailtrain installation server. ## License -**GPL-V3.0** + * Versions 1.22.0 and up **GPL-V3.0** + * Versions 1.21.0 and up: **EUPL-1.1** + * Versions 1.19.0 and up: **MIT** + * Up to versions 1.18.0 **GPL-V3.0** diff --git a/README.nitrous.md b/README.nitrous.md deleted file mode 100644 index a26dc3ac..00000000 --- a/README.nitrous.md +++ /dev/null @@ -1,13 +0,0 @@ -# Setup - -Welcome to your Mailtrain project on Nitrous. - -## Running the development server: - -In the [Nitrous IDE](https://community.nitrous.io/docs/ide-overview), start Mailtrain via "Run > Start Mailtrain". - -Now you've got a development server running and can see the output in the Nitrous terminal window. You can open up a new shell or utilize [tmux](https://community.nitrous.io/docs/tmux) to open new shells to run other commands. - -## Preview the app - -In the Nitrous IDE, open the "Preview" menu and click "Port 3000". diff --git a/app.js b/app.js index b04bd714..c6578e60 100644 --- a/app.js +++ b/app.js @@ -1,36 +1,49 @@ 'use strict'; -let config = require('config'); -let log = require('npmlog'); +const config = require('config'); +const log = require('npmlog'); -let express = require('express'); -let bodyParser = require('body-parser'); -let path = require('path'); -let favicon = require('serve-favicon'); -let logger = require('morgan'); -let cookieParser = require('cookie-parser'); -let session = require('express-session'); -let RedisStore = require('connect-redis')(session); -let flash = require('connect-flash'); -let hbs = require('hbs'); -let compression = require('compression'); -let passport = require('./lib/passport'); -let tools = require('./lib/tools'); +const _ = require('./lib/translate')._; -let routes = require('./routes/index'); -let users = require('./routes/users'); -let lists = require('./routes/lists'); -let settings = require('./routes/settings'); -let templates = require('./routes/templates'); -let campaigns = require('./routes/campaigns'); -let links = require('./routes/links'); -let fields = require('./routes/fields'); -let segments = require('./routes/segments'); -let webhooks = require('./routes/webhooks'); -let subscription = require('./routes/subscription'); -let archive = require('./routes/archive'); +const express = require('express'); +const bodyParser = require('body-parser'); +const path = require('path'); +const favicon = require('serve-favicon'); +const logger = require('morgan'); +const cookieParser = require('cookie-parser'); +const session = require('express-session'); +const RedisStore = require('connect-redis')(session); +const flash = require('connect-flash'); +const hbs = require('hbs'); +const handlebarsHelpers = require('./lib/handlebars-helpers'); +const compression = require('compression'); +const passport = require('./lib/passport'); +const tools = require('./lib/tools'); -let app = express(); +const routes = require('./routes/index'); +const users = require('./routes/users'); +const lists = require('./routes/lists'); +const settings = require('./routes/settings'); +const settingsModel = require('./lib/models/settings'); +const templates = require('./routes/templates'); +const campaigns = require('./routes/campaigns'); +const links = require('./routes/links'); +const fields = require('./routes/fields'); +const forms = require('./routes/forms'); +const segments = require('./routes/segments'); +const triggers = require('./routes/triggers'); +const webhooks = require('./routes/webhooks'); +const subscription = require('./routes/subscription'); +const archive = require('./routes/archive'); +const api = require('./routes/api'); +const blacklist = require('./routes/blacklist'); +const editorapi = require('./routes/editorapi'); +const grapejs = require('./routes/grapejs'); +const mosaico = require('./routes/mosaico'); +const reports = require('./routes/reports'); +const reportsTemplates = require('./routes/report-templates'); + +const app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); @@ -44,13 +57,18 @@ if (config.www.proxy) { // Do not expose software used app.disable('x-powered-by'); +hbs.registerPartials(__dirname + '/views/partials'); +hbs.registerPartials(__dirname + '/views/subscription/partials/'); +hbs.registerPartials(__dirname + '/views/report-templates/partials/'); +hbs.registerPartials(__dirname + '/views/reports/partials/'); + /** * We need this helper to make sure that we consume flash messages only * when we are able to actually display these. Otherwise we might end up * in a situation where we consume a flash messages but then comes a redirect * and the message is never displayed */ -hbs.registerHelper('flash_messages', function() { // eslint-disable-line prefer-arrow-callback +hbs.registerHelper('flash_messages', function () { // eslint-disable-line prefer-arrow-callback if (typeof this.flash !== 'function') { // eslint-disable-line no-invalid-this return ''; } @@ -69,7 +87,9 @@ hbs.registerHelper('flash_messages', function() { // eslint-disable-line prefer- let rows = []; messages[key].forEach(message => { - rows.push(hbs.handlebars.escapeExpression(message)); + message = hbs.handlebars.escapeExpression(message); + message = message.replace(/(\r\n|\n|\r)/gm, '
'); + rows.push(message); }); if (rows.length > 1) { @@ -88,6 +108,9 @@ hbs.registerHelper('flash_messages', function() { // eslint-disable-line prefer- ); }); +handlebarsHelpers.registerHelpers(hbs.handlebars); + + app.use(compression()); app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); @@ -113,6 +136,11 @@ app.use(session({ })); app.use(flash()); +app.use((req, res, next) => { + req._ = str => _(str); + next(); +}); + app.use(bodyParser.urlencoded({ extended: true, limit: config.www.postsize @@ -132,9 +160,13 @@ passport.setup(app); app.use((req, res, next) => { res.locals.flash = req.flash.bind(req); res.locals.user = req.user; + res.locals.ldap = { + enabled: config.ldap.enabled, + passwordresetlink: config.ldap.passwordresetlink + }; let menu = [{ - title: 'Home', + title: _('Home'), url: '/', selected: true }]; @@ -147,7 +179,25 @@ app.use((req, res, next) => { res.locals.menu = menu; tools.updateMenu(res); - next(); + + res.locals.customStyles = config.customstyles || []; + res.locals.customScripts = config.customscripts || []; + + let bodyClasses = []; + if (req.user) { + bodyClasses.push('logged-in user-' + req.user.username); + } + res.locals.bodyClass = bodyClasses.join(' '); + + settingsModel.list(['ua_code', 'shoutout'], (err, configItems) => { + if (err) { + return next(err); + } + Object.keys(configItems).forEach(key => { + res.locals[key] = configItems[key]; + }); + next(); + }); }); app.use('/', routes); @@ -156,16 +206,28 @@ app.use('/lists', lists); app.use('/templates', templates); app.use('/campaigns', campaigns); app.use('/settings', settings); +app.use('/blacklist', blacklist); app.use('/links', links); app.use('/fields', fields); +app.use('/forms', forms); app.use('/segments', segments); +app.use('/triggers', triggers); app.use('/webhooks', webhooks); app.use('/subscription', subscription); app.use('/archive', archive); +app.use('/api', api); +app.use('/editorapi', editorapi); +app.use('/grapejs', grapejs); +app.use('/mosaico', mosaico); + +if (config.reports && config.reports.enabled === true) { + app.use('/reports', reports); + app.use('/report-templates', reportsTemplates); +} // catch 404 and forward to error handler app.use((req, res, next) => { - let err = new Error('Not Found'); + let err = new Error(_('Not Found')); err.status = 404; next(err); }); @@ -200,5 +262,4 @@ app.use((err, req, res, next) => { }); }); - module.exports = app; diff --git a/config/default.toml b/config/default.toml index 3c6f8af8..17905ecd 100644 --- a/config/default.toml +++ b/config/default.toml @@ -1,39 +1,102 @@ +# 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="nobody" -#group="nogroup" +#user="mailtrain" +#group="mailtrain" -# Process title visible in monitorin logs and process listing -title="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] -level="silly" +# 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" +# Enable HTTPS +https=false +# HTTPS certificate file name +cert="cert.pem" +# HTTPS certificate private key file name +key="key.pem" +# HTTPS ca certificate file name +#ca="ca-certificate.pem" +# HTTPS Diffie Hellman parameters (generate with openssl dhparam) +#dhparams="dhparams.pem" # 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 +# logger interface for expressjs morgan log="dev" -# is the server behind a proxy? true/false -proxy=true +# 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="localhost" 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" -# enter path for mysql command line application -command="mysql" +# The timezone configured on the MySQL server. This can be 'local', 'Z', or an offset in the form +HH:MM or -HH:MM +timezone="local" [redis] # enable to use Redis session cache or disable if Redis is not installed @@ -41,13 +104,113 @@ enabled=false host="localhost" 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=25 +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 +url="ldap://localhost:3002" +baseDN="ou=users,dc=company" +filter="(|(username={{username}})(mail={{username}}))" +#Username field in LDAP (uid/cn/username) +uidTag="username" +passwordresetlink="" +# Use a different user to bind LDAP (final bind DN will be: {{uidTag}}={{bindUser}},{{baseDN}}) +bindUser="" +bindPassword="" +#ca="self-signed-ca.pem" + +[ldapauth] +# Alternative LDAP implementation using the more popular passport-ldapauth library. +enabled=false +url="ldap://localhost:389" +# Subtree in which the searchrequest for the user is done +baseDN="ou=users,dc=company" +# What whe are searching for. This should return a single user. +filter="(|(sAMAccountName={{username}})(mail={{username}}))" +# Username field in LDAP, used to identify the user in Mailtrain (uid/cn/username/sAMAccountName) +uidTag="sAMAccountName" +passwordresetlink="" +# Credentials for the initial search operation (final bind DN will be exactly as specified) +bindUser="name@company.net" +bindPassword="mySecretPassword" +#ca="self-signed-ca.pem" + +[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" 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/docker-compose.override.yml.tmpl b/docker-compose.override.yml.tmpl new file mode 100644 index 00000000..24b48ec6 --- /dev/null +++ b/docker-compose.override.yml.tmpl @@ -0,0 +1,14 @@ +version: '2' +services: + mysql: + restart: always + redis: + restart: always + mailtrain: + build: ./ + # volumes: + # - ./:/app + ports: + - "3000:3000" + restart: always + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ba00757e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +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:latest + depends_on: + - mysql + - redis + volumes: + - 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: {} diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 00000000..00101596 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,18 @@ +#!/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" + 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 diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..fa5baf40 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +docs.mailtrain.org diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..33bcb767 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,343 @@ +# Mailtrain (v1) + +[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB). + +![](http://mailtrain.org/mailtrain.png) + +> Mailtrain requires at least **Node.js v7**. If you want to use an older version of Node.js then you should use version v1.24 of Mailtrain. You can either download it [here](https://github.com/Mailtrain-org/mailtrain/archive/v1.24.0.zip) or if using git then run `git checkout v1.24.0` before starting it + +## Features + +Mailtrain supports subscriber list management, list segmentation, custom fields, email templates, large CSV list import files, etc. + +Subscribe to Mailtrain Newsletter [here](http://mailtrain.org/subscription/EysIv8sAx) (uses Mailtrain obviously) + + +## Contents + +* [Official Partners](#official-partners) +* [Cons](#cons) +* [Requirements](#requirements) +* [Installation](#installation) +* [Upgrade](#upgrade) +* [Using Environment Variables](#using-environment-variables) +* [Subscription Widget](#subscription-widget) +* [Cloudron](#cloudron) +* [Bounce Handling](#bounce-handling) +* [Testing](#testing) +* [Translations](#translations) +* [License](#license) + +## Official Partners + +### iRedMail + +![](https://mailtrain.org/images/iredmail-logo-red-114.png) + +[iRedMail](http://www.iredmail.org/) Free, open source mail server solution + +### ZoneMTA + +Check out [ZoneMTA](https://github.com/zone-eu/zone-mta) as an alternative self hosted MTA + +## Cons + + * Beta-grade software. Several users reported success with lists of various sizes (from 100k to 1M) however there is no absolute guarantee it will always work as expected. + * Almost no documentation (there are some guides in the [Wiki](https://github.com/Mailtrain-org/mailtrain/wiki)) + +## Requirements + + * Nodejs v7+ + * MySQL v5.5 or MariaDB + * Redis. Optional, disabled by default. Used for session storage and for caching state between multiple processes. If you do not have Redis enabled then you can only use a single sender process + +## Installation + +### Simple Install (Ubuntu) + +You can download and run [install.sh](setup/install.sh) in your blank Ubuntu VPS to set up +Mailtrain and all required dependencies (including MySQL). The installation script assumes a somewhat blank server, so if this is a machine you are already using for something else, you might want to skip the automatic install and proceed manually. + +If you like living on the edge and feel adventurous you can run the installation script directly from your command line as root: + +``` +curl https://raw.githubusercontent.com/Mailtrain-org/mailtrain/master/setup/install.sh | sudo bash +``` + +Install script installs and sets up the following: + + * **Node.js** (version 6.x) + * **MariaDB** (the default version from apt-get) + * **Mailtrain** (from the master branch) on port 80 + * **ImageMagick** (the default version from apt-get) + * **UFW** firewall that blocks everything besides ports 22, 25, 80, 443 + * **[ZoneMTA](https://github.com/zone-eu/zone-mta)** to queue and deliver messages (**NB!** using ZoneMTA assumes that outgoing port 25 is open which might not be the case on some hosts like on the Google Cloud) + * **Redis** server for session cache + * **logrotate** to rotate Mailtrain log files + * **upstart** or **systemd** init script to automatically start and manage Mailtrain process + +After the install script has finished and you have received "successfully installed" message, you should have a Mailtrain instance running at http://yourdomain.com + +#### Next steps after installation + +##### 1. Change admin password + +Navigate to http://yourdomain.com where yourdomain.com is the address of your server. Click on the **Sign In** link in the right top corner of the page. Authenticate with the following credentials: + + * Username: **admin** + * Password: **test** + +Once authenticated, click on your username in the right top corner of the page and select "Account". Now you should be able to change your default password. + +##### 2. Update page configuration + +If signed in, navigate to http://yourdomain.com/settings and check that all email addresses and domain names are correct. Mailtrain default installation comes bundled with [ZoneMTA](https://github.com/zone-eu/zone-mta), so you should be able to send out messages right away. ZoneMTA even handles a lot of bounces (not all kind of bounces though) automatically so you do not have to change anything in the SMTP settings to get going. + +##### 3. Set up SPF + +If you are using the bundled ZoneMTA then you need to add your Mailtrain host to the SPF DNS record of your sending domain. So if you are sending messages as "info@example.com" then the domain "example.com" should have a SPF DNS record that points to the IP address or hostname of your Mailtrain host. Everything should work without the SPF record but setting it up correctly improves the deliverability a lot. + +##### 4. Set up DKIM + +If you are using the bundled ZoneMTA then you can provide a DKIM key to sign all outgoing messages. You can provide the DKIM private key in Mailtrain Settings page. This key is only used by ZoneMTA, so if you are using some other provider then you check your providers' documentation to see how to set up DKIM. In case of ZoneMTA you only need to open Mailtrain Settings page, scroll to DKIM config section and fill the fields for DKIM selector and DKIM private key. Everything should work without the DKIM signatures but setting it up correctly improves the deliverability a lot. + +##### 5. Set up VERP + +The bundled ZoneMTA can already handle a large amount of bounces if you use it to deliver messages but not all: namely, such bounces that happen *after* the recipient MX accepts the message for local delivery. This might happen for example when a user exists, so the MX accepts the message but the quota for that user is checked only when actually storing the message to users' mailbox. Then a bounce message is generated and sent to the original sender which in your case is the mail address you are sending your list messages from. You can catch these messages and mark such recipients manually as bounced but alternatively you can set up a VERP based bounce handler that does this automatically. In this case the sender on the message envelope would not be your actual address but a rewritten bounce address that points to your Mailtrain installation. + +To set it up you need to create an additonal DNS MX entry for a bounce domain, eg "bounces.example.com",if you are sending from "example.com". This entry should point to your Mailtrain server IP address. Next you should enable the VERP handling in Mailtrain Settings page. + +> As ZoneMTA uses envelope sender as the default for DKIM addresses, then if using VERP you need to set up DKIM to your bounce domain instead of sender domain and also store the DKIM key as "bouncedomain.selector.pem" in the ZoneMTA key folder. + +If you do not use VERP with ZoneMTA then you should get notified most of the bounces so everything should mostly work without it + +##### 6. Set up proper PTR record + +If you are using the bundled ZoneMTA then you should make sure you are using a proper PTR record for your server. For example if you use DigitalOcean then PTR is set automatically (it's the droplet name, so make sure your droplet name is the same as the domain name you are running Mailtrain from). If you use AWS then you can request setting up PTR records using [this form](https://portal.aws.amazon.com/gp/aws/html-forms-controller/contactus/ec2-email-limit-rdns-request) (requires authentication). Otherwise you would have to check from your service provider, hot to get the PTR record changed. Everything should work without the PTR record but setting it up correctly improves the deliverability a lot. + +##### 7. Ready to send! + +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. + +#### Getting your head around DKIM, DMARK, SPF and PTR + +DKIM, DMARK, SPF and PTR are DNS records which spam filters use to figure out if e-mails were really sent by you (and not by a spammer who tries to conceal his identity to be able to continue send bulks of e-mails people never subscribed for). Assuming that you use zone-mta and your e-mails are to originate from a Mailtrain installation at `mailtrain.example.com` and optionally from `mail.example.net`, to practically set all these records up you will need to: + +**1.generate a private and public DKIM key** + +```sh +mkdir /opt/dkim-keys +chmod 700 /opt/dkim-keys +pushd /opt/dkim-keys +openssl genrsa -out mailtrain.example.com.key 2048 # private key mailtrain.example.com.key +openssl rsa -in mailtrain.example.com.key -out mailtrain.example.com.pub -pubout -outform PEM # public key mailtrain.example.com.pub +``` + +**2.add the three new txt records for the mailtrain.example.com that will most likely look similar to the example below** + +``` +default._domainkey.mailtrain.example.com TXT "k=rsa; p=[public key in one line];" +mailtrain.example.com TXT "v=spf1 mx a a:mail.example.net -all" +_dmarc.mailtrain.example.com TXT "v=DMARC1; p=reject" +``` + +(refer to a google search for a DKIM generator, SPF generator and DMARC genreator to get you up to speed). Configure your Mailtrain settings accoring to this: + +**DKIM domain:** mailtrain.example.com +**DKIM selector:** default +**DKIM Private Key:** [copy and paste the private key in /opt/dkim-keys/mailtrain.example.com.key] + +The above steps will have the following effect: + +- all messages sent by Mailtrain / Zone-mta will be signed by the DKIM Private Key (the signature becomes a part of the e-mail) +- when a spamfilter encounters this signature, it will look for the ****._domainkey.**** TXT record, and use the public key stored there to verify that the signature is valid +- additionally, the spamfilter will look for a TXT SPF record and will look if the e-mail was sent from the IP address of mailtrain.example.com or mail.example.net. If the sender IP or domain is different, it will discard the e-mail as spam. +- furthermore, the spamfilter looks for the DMARC record, which tells it what to do with mails that aren't signed with DKIM or which don't have a valid signature. The example above will tell the spamfilter to reject such a mail as well. + +You are now almost set. To further confirm that you have full control over your network, the last step is to set up a PTR record, which will give the right answer for a reverse DNS lookup (answer to "what domain name is bound to IP address xxx.xxx.xxx.xxx). If you run your own DNS, you probably know it will look similar to this: + +``` +10.27/1.110.220.in-addr.arpa. 1800 PTR mailtrain.example.com. +``` + +If you run Mailtrain on a VPS, you will have to find the PTR configuration somewhere in your administration interface or ask your provider to help you. + + +### Simple Install (Docker) +#### Requirements: + + * [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` 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) + + 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 `npm install --production` in the Mailtrain folder to install required dependencies + 3. Copy [config/default.toml](config/default.toml) as `config/production.toml` and update MySQL and any other settings in it + 4. Run the server `NODE_ENV=production npm start` + 5. Open [http://localhost:3000/](http://localhost:3000/) + 6. Authenticate as `admin`:`test` + 7. Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration + 8. Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password + +## Upgrade + + * Replace old files with new ones by running in the Mailtrain folder `git pull origin master` if you used Git to set Mailtrain up or just download [new files](https://github.com/Mailtrain-org/mailtrain/archive/master.zip) and replace old ones with these + * Run `npm install --production` in the Mailtrain folder + +## Using Environment Variables + +Some servers expose custom port and hostname options through environment variables. To support these, create a new configuration file `config/local.js`: + +``` +module.exports = { + www: { + port: process.env.OPENSHIFT_NODEJS_PORT, + host: process.env.OPENSHIFT_NODEJS_IP + } +}; +``` + +Mailtrain uses [node-config](https://github.com/lorenwest/node-config) for configuration management and thus the config files are loaded in the following order: + + 1. default.toml + 2. {NODE_ENV}.toml (eg. development.toml or production.toml) + 3. local.js + +### Running Behind Nginx Proxy + +Edit [mailtrain.nginx](../setup/mailtrain-nginx.conf) (update `server_name` directive) and copy it to `/etc/nginx/sites-enabled` + +### Running as an Upstart Service in Ubuntu 14.04 + +Edit [mailtrain.conf](../setup/mailtrain.conf) (update application folder) and copy it to `/etc/init` + +## Subscription Widget + +The (experimental) Mailtrain Subscription Widget allows you to embed your sign-up forms on your website. To embed a Widget, you need to: + +Enable cross-origin resource sharing in your `config` file and whitelist your site: + +``` +[cors] +# Allow subscription widgets to be embedded +origins=['https://www.example.com'] +``` + +Embed the widget code on your website: + +``` + + +``` + +## Cloudron + +You can easily install and self-host Mailtrain on the Cloudron to send newsletters from your custom domain: + +[![Install](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.mailtrain.cloudronapp) + +The source code for the Cloudron app is [here](https://git.cloudron.io/cloudron/mailtrain-app). + +## Bounce Handling + +Mailtrain uses webhooks integration to detect bounces and spam complaints. Currently supported webhooks are: + + * **AWS SES** – create a SNS topic for complaints and bounces and use `http://domain/webhooks/aws` as the subscriber URL for these topics + * **SparkPost** – use `http://domain/webhooks/sparkpost` as the webhook URL for bounces and complaints ([instructions](https://github.com/Mailtrain-org/mailtrain/wiki/Setting-up-Webhooks-for-SparkPost)) + * **SendGrid** – use `http://domain/webhooks/sendgrid` as the webhook URL for bounces and complaints ([instructions](https://github.com/Mailtrain-org/mailtrain/wiki/Setting-up-Webhooks-for-SendGrid)) + * **Mailgun** – use `http://domain/webhooks/mailgun` as the webhook URL for bounces and complaints ([instructions](https://github.com/Mailtrain-org/mailtrain/wiki/Setting-up-Webhooks-for-Mailgun)) + * **ZoneMTA** – use `http://domain/webhooks/zone-mta` as the webhook URL for bounces. If you install Mailtrain with the included installation script then this route gets set up automatically during the installation process + * **Postfix** – This is not a webhook but a TCP server on port 5699 to listen for piped Postfix logs. Enable it with the `[postfixbounce]` config option. To use it, pipe the log to that port using *tail*: `tail -F /var/log/mail.log | nc localhost 5699 -` (if Mailtrain restarts then you need to re-establish the *tail* pipe), alternatively you could send the log with a cron job periodically `tail -n 100 | nc localhost 5699 -`. + +Additionally Mailtrain (v1.1+) is able to use VERP-based bounce handling. This would require to have a compatible SMTP relay (the services mentioned above strip out or block VERP addresses in the SMTP envelope) and you also need to set up special MX DNS name that points to your Mailtrain installation server. + +If using VERP with iRedMail, see [this post](http://www.iredmail.org/forum/post49325.html#p49325) for correct configuration as iRedMail blocks by default senders that do not match authentication username (VERP address and user account address are different). + +## Testing + +There is a built in /dev/null server in Mailtrain that you can use to load-test your installation. Check the `[testserver]` section in the configuration file for details. By default, the test server is disabled. The server uses only cleartext connections, so select: "Do not use encryption" in the encryption settings when setting up the server data in Mailtrain. + +Additionally you can generate CSV import files with fake subscriber data: + +``` +node setup/fakedata.js > somefile.csv +``` + +This command generates a CSV file with 100 000 subscriber accounts + +## Translations + +Mailtrain is currently not translated but it supports translations. To add translations you first need to add translation support for the translatable strings. To test if strings are translatable or not, use a fake language with the code "zz". + +```toml +language="zz" +``` + +This would modify all input strings. If a string is not modified then it does not support translations. + +![](https://cldup.com/qXxAbaq2F1.png) + +### Translating JavaScript Files + +To translate JavaScript strings you need to make sure that you have loaded the translating function `_` from *'./lib/translate.js'*. If you want to use variables in strings then you also need the *'util'* module. + +```javascript +const _ = require('./path/to/lib/translate')._; +const util = require('util'); // optional +``` + +All you need to do to translate strings is to enclose these in the `_()` function + +```javascript +let str1 = _('This string will be translated'); +let str2 = util.format( _('My name is "%s"'), 'Mailtrain'); +``` + +### Translating Handlebars Files + +Enclose translatable strings to `{{#translate}}` tags + +```handlebars +

+ Mailtrain – {{#translate}}the best newsletter app{{/translate}} +

+``` + +### Managing Translations + +* Translations are loaded from Gettext MO files. In order to generate such files you need a Gettext translations editor. [POEdit](https://poedit.net/) is a great choice. + +* To create the translation catalog run `grunt` from command line. This fetches all translatable strings from JavaScript and Handlebars files and merges these into the translation catalog located at */languages/mailtrain.pot* + +* To add a new language use this catalog file as a source. Once you want to update your translation file from the updated catalog, select "Catalogue" -> "Update from POT file..." in POEdit and select mailtrain.pot. This will merge all the new translations from the POT file to your PO file. + + *If you have saved the PO file in [./languages](./languages) then POEdit should auto generate required MO file whenever you hit save for the PO file. + +* Once you have a correct MO file in the languages folder, edit Mailtrain config and set ["language" option](https://github.com/Mailtrain-org/mailtrain/blob/ba8bd1212335cb9bd7ba094beb7b5400f35cae6c/config/default.toml#L30-L31) to your language name. If the value is "et" then Mailtrain loads translations from ./languages/et.mo + +> **NB!** For now translation settings are global, so if you have set a translation in config then this applies to all users. An user can't select another translation than the default even if there is a translation file. This is because current Mailtrain code does not provide request context to functions and the functions generating strings do not know which language to use. + +## License + + * Versions 1.22.0 and up **GPL-V3.0** + * Versions 1.21.0 and up: **EUPL-1.1** + * Versions 1.19.0 and up: **MIT** + * Up to versions 1.18.0 **GPL-V3.0** diff --git a/index.js b/index.js index 565119fa..9428aaf4 100644 --- a/index.js +++ b/index.js @@ -4,15 +4,25 @@ * Module dependencies. */ -let config = require('config'); -let log = require('npmlog'); -let app = require('./app'); -let http = require('http'); -let sender = require('./services/sender'); -let importer = require('./services/importer'); -let verpServer = require('./services/verp-server'); -let testServer = require('./services/test-server'); -let dbcheck = require('./lib/dbcheck'); +const config = require('config'); +const log = require('npmlog'); +const app = require('./app'); +const http = require('http'); +const https = require('https'); +const fs = require('fs'); +const fork = require('child_process').fork; +const triggers = require('./services/triggers'); +const importer = require('./services/importer'); +const verpServer = require('./services/verp-server'); +const testServer = require('./services/test-server'); +const postfixBounceServer = require('./services/postfix-bounce-server'); +const tzupdate = require('./services/tzupdate'); +const feedcheck = require('./services/feedcheck'); +const dbcheck = require('./lib/dbcheck'); +const tools = require('./lib/tools'); +const reportProcessor = require('./lib/report-processor'); +const executor = require('./lib/executor'); +const privilegeHelpers = require('./lib/privilege-helpers'); let port = config.www.port; let host = config.www.host; @@ -25,11 +35,15 @@ log.level = config.log.level; app.set('port', port); /** - * Create HTTP server. + * Create HTTP/HTTPS server. */ -let server = http.createServer(app); - +let server = (!config.www.https) ? http.createServer(app) : https.createServer({ + cert: fs.readFileSync(config.www.cert), + key: fs.readFileSync(config.www.key), + ca: config.www.ca ? fs.readFileSync(config.www.ca) : undefined, + dhparams: config.www.dhparams ? fs.readFileSync(config.www.dhparams) : undefined +}, app); // Check if database needs upgrading before starting the server dbcheck(err => { @@ -64,35 +78,78 @@ server.on('error', err => { } }); +function spawnSenders(callback) { + let processes = Math.max(Number(config.queue.processes) || 1, 1); + let spawned = 0; + let returned = false; + + if (processes > 1 && !config.redis.enabled) { + log.error('Queue', '%s processes requested but Redis is not enabled, spawning 1 process', processes); + processes = 1; + } + + let spawnSender = function () { + if (spawned >= processes) { + if (!returned) { + returned = true; + return callback(); + } + return false; + } + + let child = fork(__dirname + '/services/sender.js', []); + let pid = child.pid; + tools.workers.add(child); + + child.on('close', (code, signal) => { + spawned--; + tools.workers.delete(child); + log.error('Child', 'Sender process %s exited with %s', pid, code || signal); + // Respawn after 5 seconds + setTimeout(() => spawnSender(), 5 * 1000).unref(); + }); + + spawned++; + setImmediate(spawnSender); + }; + + spawnSender(); +} + server.on('listening', () => { let addr = server.address(); let bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; log.info('Express', 'WWW server listening on %s', bind); // start additional services - testServer(() => { - verpServer(() => { - importer(() => { - sender(() => { - log.info('Service', 'All services started'); - if (config.group) { - try { - process.setgid(config.group); - log.info('Service', 'Changed group to "%s" (%s)', config.group, process.getgid()); - } catch (E) { - log.info('Service', 'Failed changed group to "%s"', config.group); - } - } - if (config.user) { - try { - process.setuid(config.user); - log.info('Service', 'Changed user to "%s" (%s)', config.user, process.getuid()); - } catch (E) { - log.info('Service', 'Failed changed user to "%s"', config.user); - } - } + function startNextServices() { + testServer(() => { + verpServer(() => { + + privilegeHelpers.dropRootPrivileges(); + + tzupdate(() => { + importer(() => { + triggers(() => { + spawnSenders(() => { + feedcheck(() => { + postfixBounceServer(() => { + reportProcessor.init(() => { + log.info('Service', 'All services started'); + }); + }); + }); + }); + }); + }); }); }); }); - }); + } + + if (config.reports && config.reports.enabled === true) { + executor.spawn(startNextServices); + } else { + startNextServices(); + } }); diff --git a/languages/de_DE.mo b/languages/de_DE.mo new file mode 100644 index 00000000..419808d4 Binary files /dev/null and b/languages/de_DE.mo differ diff --git a/languages/de_DE.po b/languages/de_DE.po new file mode 100644 index 00000000..b5f10985 --- /dev/null +++ b/languages/de_DE.po @@ -0,0 +1,4700 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-05-04 00:45+0200\n" +"PO-Revision-Date: 2018-03-07 14:12+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: de_DE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.0.6\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 "Selbst gehostete Newsletter-App" + +#: 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 "Home" + +#: views/blacklist.hbs:2 views/blacklist.hbs:3 views/layout.hbs:7 +#: views/lists/subscription/edit.hbs:15 +msgid "Blacklist" +msgstr "Blacklist" + +#: views/blacklist.hbs:4 views/users/api.hbs:55 +msgid "Add email to blacklist" +msgstr "E-Mail Adresse zur Blacklist hinzufügen" + +#: views/blacklist.hbs:5 +msgid "Add" +msgstr "Hinzufügen" + +#: views/blacklist.hbs:6 +msgid "Email" +msgstr "E-Mail" + +#: 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:126 routes/campaigns.js:35 +msgid "Campaigns" +msgstr "Kampagnen" + +#: views/campaigns/blacklisted.hbs:3 views/campaigns/blacklisted.hbs:4 +msgid "Blacklisted info" +msgstr "Blacklisted info" + +#: 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 "Kampagne ansehen" + +#: views/campaigns/blacklisted.hbs:6 +msgid "Subscribers who blacklisted by global blacklist:" +msgstr "Abonnenten, die von der globalen Blacklist aufgelistet wurden:" + +#: 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 "Adresse" + +#: 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 "Vorname" + +#: 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 "Nachname" + +#: views/campaigns/blacklisted.hbs:10 +msgid "Reason" +msgstr "Grund" + +#: views/campaigns/blacklisted.hbs:11 +msgid "Time" +msgstr "Zeit" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Bounced Info" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Abonnenten, die bounced und abgemeldet wurden:" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "SMTP Antwort" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Bounce Zeitpunkt" + +#: views/campaigns/campaigns.hbs:3 views/campaigns/create-triggered.hbs:25 +#: views/campaigns/create.hbs:3 views/campaigns/create.hbs:4 +#: views/campaigns/create.hbs:27 +msgid "Create Campaign" +msgstr "Kampagne erstellen" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Normale Kampagne" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "RSS-Kampagne" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "Trigger-Kampagne" + +#: 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 "Name" + +#: 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 "Beschreibung" + +#: 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 "Status" + +#: 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 "Erstellt" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Link Info" + +#: 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 "Klicks" + +#: views/campaigns/clicked.hbs:8 views/campaigns/view.hbs:65 +msgid "% of clicks" +msgstr "% der Klicks" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:66 +msgid "% of messages" +msgstr "% der Nachrichten" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:69 +msgid "Aggregated clicks" +msgstr "Aggregierte Klicks" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Abonnenten welche einen Link geklickt haben:" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Abonnenten welche diesen Link geklickt haben:" + +#: views/campaigns/clicked.hbs:13 views/campaigns/opened.hbs:7 +msgid "Stats by country" +msgstr "Statistik: Land" + +#: views/campaigns/clicked.hbs:14 views/campaigns/opened.hbs:8 +msgid "Stats by device type" +msgstr "Statistik: Gerätetyp" + +#: views/campaigns/clicked.hbs:18 +msgid "First click time" +msgstr "Erste Klickzeit" + +#: views/campaigns/clicked.hbs:19 +msgid "Click count" +msgstr "Anzahl Klicks" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Beschwerde Info" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Abonnenten welche sich beschwert haben und deabonniert wurden:" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Zeitpunkt der Beschwerde" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:20 +msgid "Create RSS Campaign" +msgstr "RSS-Kampagne erstellen" + +#: 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 "" +"Eine RSS-Kampagne setzt einen Tracker auf den gewählten RSS-Feed. Wenn ein " +"neuer Eintrag in diesem Feed gefunden wird, wird er als E-Mail an die " +"ausgewählte Liste gesendet." + +#: 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 "Kampagnen Name" + +#: 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 "HTML ist erlaubt" + +#: 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 "Liste" + +#: 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 "Bitte wählen" + +#: 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 "Abonnenten" + +#: 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 "" +"Neue Beiträge dieser RSS URL werden den Abonnenten als E-Mail-Nachrichten " +"zugestellt" + +#: 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 "E-Mail \"von Name\"" + +#: 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 "Dies ist der Absendername Ihrer E-Mails" + +#: 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 "E-Mail \"von Adresse\"" + +#: 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 "Dies ist die Reply-To Adresse" + +#: views/campaigns/create-rss.hbs:19 views/campaigns/create-triggered.hbs:24 +#: views/campaigns/create.hbs:28 views/campaigns/edit-rss.hbs:22 +#: views/campaigns/edit-triggered.hbs:22 views/campaigns/edit.hbs:27 +msgid "Disable opened tracking" +msgstr "Deaktiviere das Tracking beim Öffnen der E-Mail (Zählpixel)" + +#: views/campaigns/create-rss.hbs:20 views/campaigns/create-triggered.hbs:25 +#: views/campaigns/create.hbs:29 views/campaigns/edit-rss.hbs:23 +#: views/campaigns/edit-triggered.hbs:23 views/campaigns/edit.hbs:28 +msgid "Disable clicked tracking" +msgstr "Deaktiviere das Tracking beim Klicken von Links" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "Trigger-Kampagne erstellen" + +#: 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 "Vorlage" + +#: views/campaigns/create-triggered.hbs:13 views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "Vorlage auswählen:" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "" +"Wenn Sie eine Vorlage auswählen, wird eine Kopie nur für diese Kampagne erstellt" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "" +"Oder alternativ können Sie eine URL als Quelle für den E-Mail Inhalt verwenden:" + +#: views/campaigns/create-triggered.hbs:17 views/campaigns/create.hbs:17 +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:28 +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 "" +"Wenn eine Nachricht gesendet wird, wird diese URL per POST Request " +"aufgerufen mit den Merge-Tags als POST-Body. Verwenden Sie diese Funktion, " +"wenn Sie die HTML-Nachricht selbst generieren möchten" + +#: 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 "E-Mail Betreffzeile" + +#: 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 "Halten Sie den Inhalt relevant und frei von 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 "" +"Dies ist die Adresse, an welche Antworten gesendet werden, ausser die " +"Kampagnenspezifische \"reply-to\" Adresse ist definiert" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "E-Mail \"reply-to\" Adresse" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "" +"Falls gesetzt, ist dies die Adresse an welche E-Mail-Antworten gesendet " +"werden" + +#: views/campaigns/create.hbs:26 views/campaigns/edit.hbs:25 +msgid "Custom unsubscribe (URL)" +msgstr "Benutzerdefiniertes Deabonnement (URL)" + +#: views/campaigns/create.hbs:27 views/campaigns/edit.hbs:26 +msgid "Set a custom unsubscribe url" +msgstr "Geben Sie eine eigene URL an, die den Abonnenten zum Abmelden angezeigt wird" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "Zustellungs Info" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" +"Abonnenten, welche die Nachricht erfolgreich erhalten (kein \"bounce\") und " +"nicht deabonniert haben:" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "Zugestellt am" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "RSS-Kampagne bearbeiten" + +#: 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 "Allgemeine Einstellungen" + +#: 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 "" +"Verwenden Sie den speziellen Merge-Tag [RSS ENTRY], um die Position für den " +"RSS-Beitrag zu markieren. Zusätzlich können Sie auch alle gültigen Merge-" +"Tags verwenden." + +#: views/campaigns/edit-rss.hbs:23 views/campaigns/edit-triggered.hbs:26 +#: views/campaigns/edit.hbs:34 +msgid "Delete Campaign" +msgstr "Kampagne löschen" + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:35 views/lists/edit.hbs:17 +#: views/lists/fields/edit.hbs:39 views/lists/forms/edit.hbs:29 +#: 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 "Aktualisieren" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "Trigger-Kampagne bearbeiten" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +#: routes/forms.js:143 +msgid "General" +msgstr "Generell" + +#: views/campaigns/edit-triggered.hbs:23 views/campaigns/edit.hbs:26 +msgid "Template Settings" +msgstr "Vorlagen Einstellungen" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:27 +msgid "Template URL" +msgstr "Vorlagen URL" + +#: 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 "Kampagne bearbeiten" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:29 +msgid "Attachments" +msgstr "Anhänge" + +#: views/campaigns/edit.hbs:30 +msgid "File" +msgstr "Datei" + +#: views/campaigns/edit.hbs:31 +msgid "Size" +msgstr "Grösse" + +#: views/campaigns/edit.hbs:32 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 "Keine Daten verfügbar in dieser Tabelle" + +#: views/campaigns/edit.hbs:33 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "Datei hinzufügen" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "Öffnungs Info" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "Abonnenten, die diese Nachricht geöffnet haben:" + +#: views/campaigns/opened.hbs:12 +msgid "First open" +msgstr "Erstmals geöffnet" + +#: views/campaigns/opened.hbs:13 +msgid "Opened count" +msgstr "Anzahl Öffnungen" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "Deabonnement Info" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "Abonnenten welche deabonnierten:" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:28 +#: views/lists/subscription/import.hbs:10 routes/lists.js:202 +msgid "Unsubscribed" +msgstr "Deabonniert" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "Hochladen" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "Übersicht" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "Links" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "Feed URL" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "Letzter check" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "Noch nicht geprüft" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "" +"Aktivieren Sie die Kampagne, um die Überprüfung des Feeds auf neue " +"Nachrichten zu starten" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "RSS Status" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "Kampagnen Vorschau als" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "Neuen Testbenutzer hinzufügen" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "Noch keine Testbenutzer vorhanden, erstellen Sie einen hier" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "Los" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:26 +msgid "Delivered" +msgstr "Zugestellt" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "Abonnenten dieser Liste, die diese Nachricht erhalten haben" + +#: views/campaigns/view.hbs:22 +msgid "Blacklisted" +msgstr "Blacklisted" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who blacklisted by global blacklist" +msgstr "Abonnenten, die von der globalen Blacklist aufgelistet wurden" + +#: views/campaigns/view.hbs:24 routes/lists.js:202 +msgid "Bounced" +msgstr "Bounced" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who bounced" +msgstr "Bounced Listen Abonnenten" + +#: views/campaigns/view.hbs:26 +msgid "Complaints" +msgstr "Beschwerden" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who complained for this message" +msgstr "Abonnenten, die sich über diese Nachricht beschwert haben" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who unsubscribed after this message" +msgstr "Abonnenten, die sich nach dieser Nachricht abgemeldet haben" + +#: views/campaigns/view.hbs:30 +msgid "Opened" +msgstr "Geöffnet" + +#: views/campaigns/view.hbs:31 +msgid "List subscribers who opened this message" +msgstr "Abonnenten, die diese Nachricht geöffnet haben" + +#: views/campaigns/view.hbs:32 +msgid "Clicked" +msgstr "Geklickt" + +#: views/campaigns/view.hbs:33 views/campaigns/view.hbs:70 +msgid "List subscribers who clicked on a link" +msgstr "Abonnenten, die auf einen Link geklickt haben" + +#: views/campaigns/view.hbs:34 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" +"Sind Sie sicher? Diese Aktion wird mit dem Senden von Nachrichten an die " +"ausgewählte Liste beginnen" + +#: views/campaigns/view.hbs:35 +msgid "Delay sending" +msgstr "Senden verzögern" + +#: views/campaigns/view.hbs:36 +msgid "hours" +msgstr "Stunden" + +#: views/campaigns/view.hbs:37 +msgid "minutes" +msgstr "Minuten" + +#: views/campaigns/view.hbs:38 +msgid "Send to subscribers:" +msgstr "An Abonnenten senden:" + +#: views/campaigns/view.hbs:39 +msgid "Are you sure? This action would reset scheduling" +msgstr "Sind Sie sicher? Diese Aktion wird die Terminierung zurücksetzen" + +#: views/campaigns/view.hbs:40 +msgid "Cancel" +msgstr "Abbrechen" + +#: views/campaigns/view.hbs:41 +msgid "Sending scheduled" +msgstr "Senden geplant" + +#: views/campaigns/view.hbs:42 views/campaigns/view.hbs:54 +msgid "Pause" +msgstr "Pause" + +#: views/campaigns/view.hbs:43 routes/campaigns.js:253 +msgid "Sending" +msgstr "Am senden" + +#: 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 "" +"Sind Sie sicher? Diese Aktion wird das Senden von E-Mails an die " +"ausgewählte Liste fortsetzen" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" +"Sind Sie sicher? Diese Aktion würde alle Statistiken über den aktuellen " +"Fortschritt zurücksetzen" + +#: views/campaigns/view.hbs:46 +msgid "Resume" +msgstr "Fortsetzen" + +#: views/campaigns/view.hbs:47 views/campaigns/view.hbs:51 +msgid "Reset" +msgstr "Zurücksetzen" + +#: views/campaigns/view.hbs:50 +msgid "Continue" +msgstr "Weiter" + +#: views/campaigns/view.hbs:52 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" +"Alle E-Mails gesendet! Klicken Sie auf \"Weiter\", wenn Sie diese Kampagne " +"an neue Abonnenten senden möchten" + +#: 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 "" +"Sind Sie sicher? Diese Aktion wird das Senden neuer Einträge des RSS-Feed " +"als E-Mail-Nachrichten an die ausgewählte Liste pausieren" + +#: views/campaigns/view.hbs:55 views/campaigns/view.hbs:59 +msgid "Campaign status:" +msgstr "Kampagnen Status:" + +#: views/campaigns/view.hbs:56 +msgid "ACTIVE" +msgstr "AKTIV" + +#: 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 "" +"Sind Sie sicher? Diese Aktion würde neue RSS Feed Einträge als E-Mail-" +"Nachrichten der ausgewählten Liste zustellen" + +#: views/campaigns/view.hbs:58 +msgid "Activate" +msgstr "Aktivieren" + +#: views/campaigns/view.hbs:60 +msgid "INACTIVE" +msgstr "INAKTIV" + +#: 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 "" +"Dies ist eine Trigger-Kampagne. Nachrichten werden nur an Abonnenten " +"gesendet, die einen Trigger auslösen, der diese Kampagne aufruft" + +#: views/campaigns/view.hbs:62 +msgid "see more" +msgstr "mehr anzeigen" + +#: views/campaigns/view.hbs:67 +msgid "List subscribers who clicked this link" +msgstr "Abonnenten dieser Liste, die diesen Link geklickt haben" + +#: 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 "" +"Klicks werden als eindeutige Abonnenten gezählt, die auf einen bestimmten " +"Link oder auf irgendeinen Link geklickt haben (in der aggregierter Ansicht)" + +#: 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 "" +"Wenn ein neuer Eintrag aus dem Kampagnen-Feed gefunden wird, wird eine neue " +"Unterkampagne von diesem Eintrag erstellt und hier aufgelistet" + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "Ändern Sie Ihr Passwort" + +#: 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 "" +"Wir haben eine Anforderung zur Passwortänderungs für Ihr Mailtrain-Konto erhalten:" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "Passwort zurücksetzen" + +#: 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 "" +"Wenn Sie Ihr Passwort nicht ändern wollen, können Sie diese E-" +"Mail einfach ignorieren." + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "Persönliche Einstellungen" + +#: views/emails/rss-html.hbs:2 views/emails/stationery-html.hbs:4 +#: views/emails/stationery-text.hbs:4 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:3 routes/forms.js:213 +#: routes/lists.js:284 +msgid "Unsubscribe" +msgstr "Newsletter abbestellen" + +#: 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 "E-Mail im Browser ansehen" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "Hallo [FIRST_NAME/Customer]," + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "Viele Grüße," + +#: views/index.hbs:1 +msgid "List Management" +msgstr "Listen Management" + +#: 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 ermöglicht es Ihnen, auch sehr große Listen zu verwalten. Millionen von " +"Abonnenten? Kein Problem. Sie können Abonnenten manuell über die API " +"hinzufügen oder aus einer CSV-Datei importieren. Für alle Listen können " +"zusätzliche Datenfelder und Merge Tags genutzt werden." + +#: 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 "Zeige mehr" + +#: 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 "Zusätzliche Datenfelder" + +#: 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 "" +"Textfelder, Nummern, Dropdowns oder Checkboxen, Mailtrain bietet all das. " +"Jedes zusätzliche Feld kann in den generierten Newslettern über " +"Merge-Tags eingebunden werden." + +#: views/index.hbs:6 +msgid "Mailtrain also supports custom forms." +msgstr "Mailtrain bietet auch benutzerdefinierte Formulare." + +#: views/index.hbs:8 +msgid "List Segmentation" +msgstr "Listen Segmentierung" + +#: 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 "" +"Senden Sie nur Nachrichten an Abonnenten, welche die vordefinierten " +"Segmentierungsregeln erfüllen. Es besteht keine Notwendigkeit, separate " +"Listen mit kleinen Unterschieden zu erstellen." + +#: views/index.hbs:11 +msgid "RSS Campaigns" +msgstr "RSS-Kampagnen" + +#: 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 "" +"Mailtrain kann RSS-Feeds verfolgen und bei neuen Einträgen automatisch " +"eine neue Kampagne mit dem RSS-Beitrag als Nachrichteninhalt an die " +"ausgewählte Liste senden." + +#: views/index.hbs:14 +msgid "GPG Encryption" +msgstr "GPG Verschlüsselung" + +#: 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 "" +"Wenn für eine Liste ein benutzerdefiniertes Feld für den GPG-Public-Key " +"vorhanden ist, können Abonnenten ihren GPG-Public-Key hochladen, um " +"alle E-Mails verschlüsselt zu erhalten." + +#: views/index.hbs:17 +msgid "Click Stats" +msgstr "Klick Statistik" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" +"Nachdem eine Kampagne gesendet wurde, sind Klick-Statistiken für jeden in " +"der E-Mail-Nachricht enthaltener Link verfügbar." + +#: views/index.hbs:20 +msgid "Template Editors" +msgstr "Vorlagen Editoren" + +#: 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 beinhaltet GrapeJS und Mosaico, zwei Programme zum Bearbeiten der " +"E-Mail-Vorlagen. Natürlich bietet Mailtrain auch einen Code-Editor, sofern " +"Sie den HTML Code selber erstellen möchten." + +#: views/index.hbs:23 +msgid "Send via Any Provider" +msgstr "Sende mit beliebigen Anbietern" + +#: 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 empfiehlt SendPulse, obwohl Sie " +"jeden Provider verwenden können, der das SMTP-Protokoll unterstützt, um Ihre " +"Newsletter zu versenden. Bounce- und Reklamationsabwicklung werden über " +"Webhooks für SES, SparkPost, SendGrid und Mailgun unterstützt, auch für " +"Postfix und ZoneMTA." + +#: views/index.hbs:26 lib/tools.js:130 +msgid "Automation" +msgstr "Automatisierung" + +#: views/index.hbs:27 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" +"Definieren Sie Automatisierungs-Trigger, um bestimmte Nachrichten zu senden, " +"wenn ein Benutzer den Trigger aktiviert." + +#: views/index.hbs:29 +msgid "Donate to Author" +msgstr "Dem Autor spenden" + +#: views/index.hbs:30 +msgid "Mailtrain is available under GPLv3 license and completely open source." +msgstr "Mailtrain ist unter GPLv3 Lizenz verfügbar und komplett offen." + +#: 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 "" +"Wenn Sie Mailtrain wirklich mögen oder Ihr Geschäft davon finanziell " +"profitiert, würde ich eine kleine Spende sehr schätzen, um die Mailtrain-" +"Entwicklung am laufen zu halten. Sie können entweder per Bitcoin oder PayPal " +"spenden. Meine Bitcoin Adresse ist" + +#: views/index.hbs:32 +msgid "Or Donate Using Paypal" +msgstr "Oder spenden Sie mit PayPal" + +#: views/index.hbs:33 +msgid "Official Mailtrain Partners" +msgstr "Offizielle Mailtrain Partner" + +#: views/index.hbs:34 +msgid "" +"A reliable SMTP server, easy integration, and 12,000 messages a month free" +msgstr "" +"Ein zuverlässiger SMTP-Server, einfache Integration und 12.000 Nachrichten " +"pro Monat kostenlos" + +#: views/index.hbs:35 +msgid "Free, open source mail server solution" +msgstr "Kostenlose, Open Source Mail Server Lösung" + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "Navigation umschalten" + +#: 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 "Account" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "Einstellungen" + +#: 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 "Abmelden" + +#: 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 "Anmelden" + +#: views/layout.hbs:11 +msgid "Self Hosted Newsletter App Built on Top of Nodemailer" +msgstr "Selbst gehostete Newsletter-App basierend auf Nodemailer" + +#: views/layout.hbs:12 views/layout.hbs:14 +msgid "Source on GitHub" +msgstr "Quellcode auf Github" + +#: views/layout.hbs:13 +msgid "Subscribe to Our Newsletter" +msgstr "Abonnieren Sie unseren Newsletter" + +#: 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:118 routes/lists.js:59 +msgid "Lists" +msgstr "Listen" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:10 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "Liste erstellen" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "Name der Liste" + +#: views/lists/create.hbs:9 views/lists/edit.hbs:15 +msgid "Allow public users to subscribe themselves" +msgstr "Allen erlauben, diese Liste selbst zu abonnieren" + +#: views/lists/create.hbs:11 views/lists/edit.hbs:17 +msgid "Unsubscription" +msgstr "Deabonnement" + +#: views/lists/create.hbs:12 views/lists/edit.hbs:18 +msgid "Select how an unsuscription request by subscriber is handled." +msgstr "" +"Wählen Sie, welche Schritte zum Abmelden von der Liste notwendig sein sollen." + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:8 +msgid "Edit List" +msgstr "Liste bearbeiten" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "Liste ansehen" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "Listen ID" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "Diese Listen ID wird den Abonnenten dargestellt" + +#: views/lists/edit.hbs:12 +msgid "Custom Form" +msgstr "Benutzerdefiniertes Formular" + +#: views/lists/edit.hbs:13 views/lists/forms/forms.hbs:11 +msgid "Default Mailtrain Form" +msgstr "Standard Mailtrain Formular" + +#: views/lists/edit.hbs:14 +msgid "" +"The custom form used for this list. You can create a form here." +msgstr "" +"Das Standard-Formular dieser Liste. Wenn Sie ein benutzerdefiniertes Formular " +"erstellen möchten, klicken Sie hier." + +#: views/lists/edit.hbs:19 +msgid "Unsubscribe Header" +msgstr "Deabonnement Header" + +#: views/lists/edit.hbs:20 +msgid "Do not send List-Unsubscribe headers" +msgstr "Sende keine Header zum Abmelden von der Liste" + +#: views/lists/edit.hbs:16 +msgid "Delete List" +msgstr "Liste löschen" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "Feld erstellen" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "zusäzliches Feld erstellen" + +#: 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 "Feldnamen" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "Feldtyp" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "Text" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "Nummer" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "Webseite" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "GPG Public Key" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "Mehrzeiliger Text" + +#: 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 "Datum" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "Datum (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 "Datum (DD/MM/YYYY)" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "Geburtstag" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "Geburtstag (MM/DD)" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "Geburtstag (DD/MM)" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "Gruppiert" + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "Dropdown" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "Radio Buttons" + +#: views/lists/fields/create.hbs:24 views/lists/fields/edit.hbs:25 +#: lib/models/fields.js:23 +msgid "Checkboxes" +msgstr "Checkboxen" + +#: views/lists/fields/create.hbs:25 views/lists/fields/edit.hbs:26 +msgid "Option for a group value" +msgstr "Option für einen Gruppenwert" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "Gruppe" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "Erforderlich für Gruppenoptionen" + +#: 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 "Standard Wert für Merge-Tag" + +#: 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 "" +"Für Gruppenelemente wie Checkboxen können Sie das Aussehen des Merge-Tags " +"mit einer optionalen Vorlage steuern. Die Vorlage verwendet die Handlebars-" +"Syntax und Sie können alle Werte des {{values}} Array finden, " +"zB {{#each}} {{this}} {{/each}}. Wenn die Vorlage nicht " +"definiert ist, werden mehrere Werte mit Kommas verbunden. Sie können diese " +"Vorlage auch verwenden, um JSON-Werte zu rendern (wenn das JSON ein Array " +"ist, dann ist das Array als values verfügbar, ansonsten können " +"Sie direkt auf die JSON-Keys zugreifen)." + +#: views/lists/fields/create.hbs:33 views/lists/fields/edit.hbs:37 +msgid "Visible" +msgstr "Sichtbar" + +#: views/lists/fields/create.hbs:34 +msgid "Add Field" +msgstr "Feld hinzufügen" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "Feld bearbeiten" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "Zusätzliches Feld bearbeiten" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "Zurück zu den Feldern" + +#: 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 "Merge-Tag" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "Merge-Tag" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "Fügen Sie diesen Tag Ihrem Inhalt hinzu:" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "Feld löschen" + +#: views/lists/fields/fields.hbs:7 views/lists/view.hbs:26 +#: views/report-templates/partials/report-template-fields.hbs:5 +msgid "Type" +msgstr "Typ" + +#: views/lists/fields/fields.hbs:10 views/lists/fields/fields.hbs:11 +#: views/lists/forms/edit.hbs:22 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:166 routes/lists.js:253 routes/report-templates.js:51 +#: routes/templates.js:170 routes/triggers.js:297 +msgid "Edit" +msgstr "Bearbeiten" + +#: 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 "Benutzerdefinierte Formulare" + +#: views/lists/forms/create.hbs:4 +msgid "Create Form" +msgstr "Formular erstellen" + +#: views/lists/forms/create.hbs:5 views/lists/forms/forms.hbs:4 +msgid "Create Custom Form" +msgstr "Benutzerdefiniertes Formular erstellen" + +#: 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 "Formular-Name" + +#: views/lists/forms/create.hbs:8 +msgid "Add Form" +msgstr "Formular hinzufügen" + +#: views/lists/forms/edit.hbs:4 +msgid "Edit Form" +msgstr "Formular bearbeiten" + +#: views/lists/forms/edit.hbs:5 +msgid "Edit Custom Form" +msgstr "Formular bearbeiten" + +#: views/lists/forms/edit.hbs:6 +msgid "Back to forms" +msgstr "Zurück zu Formulare" + +#: views/lists/forms/edit.hbs:10 +msgid "Optional comments about this form" +msgstr "Optionale Kommentare zu diesem Formular" + +#: views/lists/forms/edit.hbs:11 +msgid "Form Preview" +msgstr "Formular Vorschau" + +#: 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 "" +"Hinweis: Diese Links dienen ausschließlich der Vorschau. Wenn Sie ein " +"Vorschau-Formlar absenden, werden Sie auf das Standardformular der Liste " +"umgeleitet." + +#: views/lists/forms/edit.hbs:13 views/lists/subscription/add.hbs:16 +#: views/subscription/mail-unsubscribe-confirmed-html.mjml.hbs:4 +#: views/subscription/mail-unsubscribe-confirmed-text.hbs:4 routes/forms.js:157 +#: routes/lists.js:284 +msgid "Subscribe" +msgstr "Abonnieren" + +#: views/lists/forms/edit.hbs:14 +msgid "Confirm Notice" +msgstr "Bestätigung erforderlich" + +#: views/lists/forms/edit.hbs:15 +msgid "Updated Notice" +msgstr "Profil aktualisiert" + +#: views/lists/forms/edit.hbs:16 +msgid "Unsubscribed Notice" +msgstr "Abmeldungshinweis" + +#: views/lists/forms/edit.hbs:17 routes/forms.js:195 +msgid "Manage" +msgstr "Verwalten" + +#: views/lists/forms/edit.hbs:18 +msgid "Manage Address" +msgstr "Adresse Verwalten" + +#: views/lists/forms/edit.hbs:19 +msgid "Create a test user for additional options" +msgstr "Erstellen Sie einen Testbenutzer für zusätzliche Optionen" + +#: views/lists/forms/edit.hbs:20 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:122 routes/templates.js:27 +msgid "Templates" +msgstr "Vorlagen" + +#: views/lists/forms/edit.hbs:21 +msgid "Fields" +msgstr "Felder" + +#: views/lists/forms/edit.hbs:23 +msgid "Form Fields" +msgstr "Formular Felder" + +#: views/lists/forms/edit.hbs:24 +msgid "Fields hidden on subscription page:" +msgstr "Unsichtbare Felder auf der Anmeldung-Seite:" + +#: views/lists/forms/edit.hbs:25 +msgid "Fields shown on subscription page:" +msgstr "Sichtbare Felder auf der Anmeldung-Seite:" + +#: views/lists/forms/edit.hbs:26 +msgid "Fields hidden on preferences page:" +msgstr "Unsichtbare Felder auf der Profil-Seite:" + +#: views/lists/forms/edit.hbs:27 +msgid "Fields shown on preferences page:" +msgstr "Sichtbare Felder auf der Profil-Seite:" + +#: views/lists/forms/edit.hbs:28 +msgid "Delete Form" +msgstr "Formular löschen" + +#: views/lists/forms/forms.hbs:10 +msgid "The default form for this list is:" +msgstr "Das Standard-Formular für diese Liste ist:" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "ID" + +#: views/lists/lists.hbs:7 views/reports/partials/report-fields.hbs:10 +msgid "Subscribers" +msgstr "Abonnenten" + +#: 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 "Segmente" + +#: 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 "Segment erstellen" + +#: 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 "Segmentnamen" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "Regel match" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "Alle Regeln müssen zutreffen" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "Irgendeine Regel kann zutreffen" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "Segment hinzufügen" + +#: 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 "Segment bearbeiten" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "Zurück zu Segmente" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "Segment löschen" + +#: 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 "Regel erstellen" + +#: 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 "Regel" + +#: 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 "Wert" + +#: 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 "" +"Verwenden Sie % für Platzhalterzeichen, z.B. \"%test“, um alle Werte zu " +"matchen, die mit \"test\" enden" + +#: 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 "Exact match verwenden" + +#: 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 "Range match verwenden" + +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-edit.hbs:20 +msgid "Use relative range match" +msgstr "Relative range match verwenden" + +#: 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 "Von" + +#: 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 "Tage" + +#: 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 "vor heute" + +#: 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 "nach heute" + +#: 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 "an" + +#: 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 "Ausgewählt" + +#: 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 "Nicht ausgewählt" + +#: views/lists/segments/rule-configure.hbs:33 +msgid "Add Rule" +msgstr "Regel hinzufügen" + +#: 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 "Weiter" + +#: views/lists/segments/rule-edit.hbs:37 +msgid "Delete Rule" +msgstr "Regel löschen" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "Match" + +#: views/lists/segments/view.hbs:5 views/lists/view.hbs:13 +msgid "Segment" +msgstr "Segment" + +#: views/lists/segments/view.hbs:7 +msgid "Match rules" +msgstr "Regel match" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "Passende Abonnenten" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "zeige" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "Abonnent hinzufügen" + +#: views/lists/subscription/add.hbs:5 +#: views/subscription/partials/subscription-custom-fields.hbs:1 +#: views/users/account.hbs:7 +msgid "Email Address" +msgstr "E-Mail-Adresse" + +#: 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 "Beginnt mit" + +#: 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 "" +"Falls Sie hier einen GPG Public Key einfügen, wird dieser verwendet um E-" +"Mails an diesen Abonnenten zu verschlüsseln" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "Zeitzone" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "Test-Benutzer?" + +#: 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 "" +"Wenn diese Option aktiviert ist, kann dieser Abonnent für die Vorschau von " +"Kampagnen verwendet werden" + +#: 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 "" +"Diese Person wird keine Bestätigungs-E-Mail erhalten. Stellen Sie sicher, " +"dass Sie die Erlaubnis haben, dieser Person E-Mails zu senden." + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "Abonnent bearbeiten" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "Zurück zur Liste" + +#: views/lists/subscription/edit.hbs:6 +#: views/lists/subscription/import-preview.hbs:6 +#: views/subscription/partials/subscription-unsubscribe-form.hbs:1 +#: lib/helpers.js:41 lib/models/segments.js:11 +msgid "Email address" +msgstr "E-Mail-Adresse" + +#: views/lists/subscription/edit.hbs:17 +msgid "Delete Subscription" +msgstr "Abonnement löschen" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "Import status" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "Fehlgeschlagene Adressen" + +#: 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 "" +"Rollenbasierte Adressen wie postmaster@example.com sind beim Import " +"gesperrt. Abonnenten mit rollenbasierten E-Mail-Adressen können sich mit dem " +"Anmeldeformular Ihrer Liste abonnieren." + +#: views/lists/subscription/import-failed.hbs:7 +msgid "see here" +msgstr "sehen Sie hier" + +#: views/lists/subscription/import-failed.hbs:9 +msgid "Fail reason" +msgstr "Fehlerursache" + +#: 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 "Abonnenten importieren" + +#: 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 "Beispiel" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "Import starten" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "CVS Datei" + +#: views/lists/subscription/import.hbs:6 +msgid "CSV delimiter" +msgstr "CSV-Trennzeichen" + +#: views/lists/subscription/import.hbs:7 +msgid "Categorize the imported subscribers as" +msgstr "Kategorisiere die importierten Abonnenten als" + +#: views/lists/subscription/import.hbs:8 routes/lists.js:202 +msgid "Subscribed" +msgstr "Abonniert" + +#: views/lists/subscription/import.hbs:9 +msgid "Regular subscriber addresses" +msgstr "Normale Abonnenten-Adressen" + +#: views/lists/subscription/import.hbs:11 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "Unterdrückte E-Mail-Adressen, die von Ihrer Liste abgemeldet werden" + +#: views/lists/subscription/import.hbs:12 +msgid "Check imported emails" +msgstr "Überprüfe die importierten E-Mail-Adressen" + +#: views/lists/subscription/import.hbs:13 views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "Aktiviert" + +#: views/lists/subscription/import.hbs:14 views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "Deaktiviert" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "Abonnement Formular" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "Listen Aktionen" + +#: 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 "Trigger erstellen" + +#: views/lists/view.hbs:10 +msgid "Add Subscriber" +msgstr "Abonnent hinzufügen" + +#: views/lists/view.hbs:11 +msgid "Import Subscribers" +msgstr "Abonnenten importieren" + +#: views/lists/view.hbs:15 +msgid "Create New Segment" +msgstr "Neues Segment erstellen" + +#: views/lists/view.hbs:16 +msgid "Filter" +msgstr "Filter" + +#: views/lists/view.hbs:17 +msgid "Subscriptions" +msgstr "Abonnements" + +#: views/lists/view.hbs:18 +msgid "Imports" +msgstr "Importe" + +#: views/lists/view.hbs:25 routes/campaigns.js:255 routes/lists.js:296 +msgid "Finished" +msgstr "Fertig" + +#: views/lists/view.hbs:27 +msgid "Added" +msgstr "Hinzugefügt" + +#: views/lists/view.hbs:28 +msgid "Updated" +msgstr "Aktualisiert" + +#: views/lists/view.hbs:29 +msgid "Failed" +msgstr "Fehlgeschlagen" + +#: views/lists/view.hbs:31 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" +"Sind Sie sicher? Diese Aktion sollte nur ausgeführt werden, um Probleme mit " +"hängenden Importen zu lösen" + +#: views/lists/view.hbs:32 +msgid "Restart" +msgstr "Neustart" + +#: views/mosaico/editor.hbs:1 views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "Merge-Tag Referenz" + +#: views/mosaico/editor.hbs:4 +msgid "MOSAICO Responsive Email Designer" +msgstr "MOSAICO Responsive E-Mail-Designer" + +#: views/mosaico/editor.hbs:5 +msgid "Sucessfully saved" +msgstr "Erfolgreich gespeichert" + +#: views/mosaico/editor.hbs:6 +msgid "An error occured while saving the document" +msgstr "Beim Speichern des Dokuments ist ein Fehler aufgetreten" + +#: views/mosaico/editor.hbs:7 +msgid "Unsaved changes will be lost. Close now?" +msgstr "Nicht gespeicherte Änderungen gehen verloren. Jetzt schließen?" + +#: views/mosaico/editor.hbs:8 views/mosaico/editor.hbs:9 +msgid "Tags" +msgstr "Tags" + +#: 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 "Vorlagen-Inhalt (HTML)" + +#: views/partials/editor-navbar.hbs:1 +msgid "SAVE" +msgstr "SPEICHERN" + +#: views/partials/editor-navbar.hbs:2 +msgid "SAVING" +msgstr "AM SPEICHERN" + +#: views/partials/editor-navbar.hbs:3 +msgid "CLOSE" +msgstr "SCHLIESSEN" + +#: views/partials/grapejs.hbs:2 +msgid "Open GrapeJS" +msgstr "GrapeJS öffnen" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "HTML Vorschau anzeigen" + +#: views/partials/html-to-text.hbs:1 +msgid "" +"To extract the text from HTML click here." +msgstr "" +"Um den Klartext aus dem HTML Dokument zu extrahieren, klicken Sie hier." + +#: 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 "" +"Hierbei wird Ihr bestehender Klartext im Feld oberhalb überschreiben. Diese " +"Funktion nutzt die Premailer API. Die Verwendung unterliegt " +"deren AGB und Datenschutzbestimmungen." + +#: views/partials/html-to-text.hbs:3 +msgid "An error occurred while talking to the server" +msgstr "Beim Datenverkehr mit dem Server ist ein Fehler aufgetreten" + +#: 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 sind Tags, die vor dem Senden der Nachricht ersetzt werden. Das " +"Format des Merge-Tags lautet wie folgt: [TAG_NAME] oder " +"[TAG_NAME/fallback] wobei fallback ein optionaler " +"Textwert ist, der verwendet wird, falls TAG_NAME leer ist." + +#: views/partials/modal-carousel.hbs:1 +msgid "{{title}}" +msgstr "{{title}}" + +#: views/partials/mosaico.hbs:2 +msgid "Open Mosaico" +msgstr "Mosaico öffnen" + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "Vorlagen-Inhalt (Klartext)" + +#: 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:137 routes/reports.js:31 +msgid "Reports" +msgstr "Reporte" + +#: 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 "Vorlage erstellen" + +#: views/report-templates/create.hbs:5 routes/report-templates.js:231 +msgid "Create Report Template" +msgstr "Report-Vorlage erstellen" + +#: views/report-templates/edit.hbs:4 views/templates/edit.hbs:3 +#: views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "Vorlage bearbeiten" + +#: views/report-templates/edit.hbs:5 routes/report-templates.js:262 +msgid "Edit Report Template" +msgstr "Report-Vorlage bearbeiten" + +#: views/report-templates/edit.hbs:6 views/templates/edit.hbs:11 +msgid "Delete Template" +msgstr "Vorlage löschen" + +#: views/report-templates/edit.hbs:7 +msgid "Update and Stay" +msgstr "Aktualisieren und bleiben" + +#: views/report-templates/edit.hbs:8 +msgid "Update and Leave" +msgstr "Aktualisieren und verlassen" + +#: views/report-templates/partials/report-template-fields.hbs:2 +msgid "Template Name" +msgstr "Vorlagen-Name" + +#: views/report-templates/partials/report-template-fields.hbs:6 +msgid "User selectable fields" +msgstr "Vom Benutzer wählbare Felder" + +#: views/report-templates/partials/report-template-fields.hbs:7 +msgid "Data processing code" +msgstr "Datenverarbeitungs-Code" + +#: views/report-templates/partials/report-template-fields.hbs:8 +msgid "Rendering template" +msgstr "Render-Vorlage" + +#: views/report-templates/report-templates.hbs:5 +msgid "Blank" +msgstr "Leer" + +#: views/report-templates/report-templates.hbs:6 +msgid "All Subscribers" +msgstr "Alle Abonnenten" + +#: views/report-templates/report-templates.hbs:7 +msgid "Grouped Subscribers" +msgstr "Gruppierte Abonnenten" + +#: views/report-templates/report-templates.hbs:8 +msgid "Export List as CSV" +msgstr "Liste als CSV exportieren" + +#: views/report-templates/report-templates.hbs:9 views/reports/reports.hbs:4 +#: routes/report-templates.js:29 +msgid "Report Templates" +msgstr "Report-Vorlagen" + +#: 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 "Report erstellen" + +#: views/reports/edit.hbs:3 views/reports/edit.hbs:4 routes/reports.js:151 +msgid "Edit Report" +msgstr "Report bearbeiten" + +#: views/reports/edit.hbs:5 +msgid "Delete Report" +msgstr "Report löschen" + +#: views/reports/partials/report-fields.hbs:2 +msgid "Report Name" +msgstr "Report-Name" + +#: 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 "" +"Wählen Sie eine Kampagne in der obigen Tabelle aus, indem Sie auf die " +"jeweilige Zeilennummer klicken." + +#: views/reports/partials/report-select-template.hbs:1 +msgid "Report Template" +msgstr "Report-Vorlage" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "Service Adresse (URL)" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "Geben Sie die URL ein, auf welcher dieser Service verfügbar ist" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "Admin E-Mail" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "" +"Geben Sie hier die E-Mail-Adresse ein, die als \"von\" für Systemmeldungen " +"verwendet wird" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "WYSIWYG Editor deaktivieren" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "Wenn markiert zeigt der Editor HTML-Code ohne Vorschau an" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "Bestätigungsmeldungen für Abonnements deaktivieren" + +#: 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 "" +"Wenn markiert wird keine Bestätigungsnachricht gesendet, die besagt, dass " +"der Teilnehmer jetzt abonniert oder abgemeldet ist. Dies deaktiviert die " +"Double-Opt-In-Nachrichten nicht." + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "Tracking ID" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "Google-Analytics Tracking-Code" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "Meldung auf der Startseite" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "HTML-Code im Kopfbereich der Startseite" + +#: views/settings.hbs:17 +msgid "Campaign defaults" +msgstr "Kampagnenvorgaben" + +#: views/settings.hbs:18 +msgid "Sender name" +msgstr "Absender Name" + +#: views/settings.hbs:19 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "Absender Name, z.B. Meine grossartige Firma" + +#: views/settings.hbs:20 +msgid "Default address" +msgstr "Standardadresse" + +#: views/settings.hbs:21 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "Kontakt Adresse, z.B. 1234 Hauptstrasse, 01234 Irgendwo, Deutschland" + +#: views/settings.hbs:22 +msgid "Default \"from name\"" +msgstr "Standard \"von\" Namen" + +#: views/settings.hbs:24 +msgid "Default \"from\" email" +msgstr "Standard \"von\" E-Mail-Adresse" + +#: views/settings.hbs:26 +msgid "Default \"subject line\"" +msgstr "Standard \"Betreffzeile\"" + +#: views/settings.hbs:28 +msgid "Default homepage (URL)" +msgstr "Standard-Homepage (URL)" + +#: views/settings.hbs:29 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "" +"URL, um die abonnierten Benutzer weiterzuleiten, z.B. http://example.com/" + +#: views/settings.hbs:30 +msgid "Mailer Settings" +msgstr "Mailer-Einstellungen" + +#: views/settings.hbs:31 +msgid "These settings are required to send out e-mail messages" +msgstr "Diese Einstellungen sind erforderlich, um E-Mails zu senden" + +#: 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 "SMTP zum Senden der E-Mails verwenden" + +#: views/settings.hbs:35 +msgid "Hostname" +msgstr "Hostname" + +#: views/settings.hbs:36 +msgid "Port" +msgstr "Port" + +#: views/settings.hbs:37 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "Port, z.B: 465. Automatisch erkannt wenn leer" + +#: views/settings.hbs:38 +msgid "Encryption" +msgstr "Verschlüsseleung" + +#: views/settings.hbs:39 +msgid "Disable SMTP authentication" +msgstr "SMTP Authentifizierung deaktivieren" + +#: views/settings.hbs:40 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "Benutzername" + +#: views/settings.hbs:41 +msgid "Username, eg. myaccount@example.com" +msgstr "Benutzername, z.B: myaccount@example.com" + +#: views/settings.hbs:42 views/settings.hbs:43 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "Passwort" + +#: views/settings.hbs:44 +msgid "Use SES API for sending mail" +msgstr "SES API zum Versenden der E-Mails verwenden" + +#: views/settings.hbs:45 +msgid "Access Key" +msgstr "Access Key" + +#: views/settings.hbs:46 +msgid "AWS Access Key Id" +msgstr "AWS Access Key Id" + +#: views/settings.hbs:47 +msgid "Secret Key" +msgstr "Secret Key" + +#: views/settings.hbs:48 +msgid "AWS Secret Access Key" +msgstr "AWS Secret Access Key" + +#: views/settings.hbs:49 +msgid "Region" +msgstr "Region" + +#: views/settings.hbs:50 +msgid "Checking" +msgstr "Am überprüfen" + +#: views/settings.hbs:51 +msgid "Check Mailer config" +msgstr "Mailer-Konfiguration überprüfen" + +#: views/settings.hbs:52 +msgid "Don't have an SMTP account yet? Create a free SendPulse account" +msgstr "" +"Haben Sie noch kein SMTP-Konto? Erstellen Sie einen kostenloses SendPulse-" +"Account" + +#: views/settings.hbs:53 +msgid "here" +msgstr "hier" + +#: views/settings.hbs:54 +msgid "Advanced Mailer settings" +msgstr "Erweiterte Mailer-Einstellungen" + +#: views/settings.hbs:55 +msgid "Log SMTP transactions" +msgstr "SMTP-Transaktionen Loggen" + +#: views/settings.hbs:56 +msgid "Allow self-signed certificates" +msgstr "Selbstsignierte Zertifikate zulassen" + +#: views/settings.hbs:57 +msgid "Max connections" +msgstr "Max. Verbindungen" + +#: views/settings.hbs:58 +msgid "The count of max connections, eg. 10" +msgstr "Die Anzahl maximaler Verbindungen, z.B. 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 "" +"Anzahl der maximalen parallelen Verbindungen zum SMTP-Server (standardmäßig " +"auf 5). Diese Limite gilt per Sendungsprozess." + +#: views/settings.hbs:60 +msgid "Max messages" +msgstr "Max. Nachrichten" + +#: views/settings.hbs:61 +msgid "The count of max messages, eg. 100" +msgstr "Die Anzahl maximaler Nachrichten, z.B. 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 "" +"Die Anzahl der Nachrichten, die durch eine einzige Verbindung gesendet " +"werden sollen, bevor die Verbindung geschlossen und wieder geöffnet wird " +"(standardmäßig 100)" + +#: views/settings.hbs:63 +msgid "Throttling" +msgstr "Throttling" + +#: views/settings.hbs:64 +msgid "Messages per hour eg. 1000" +msgstr "E-Mail-Nachrichten pro Stunde z.B. 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 "" +"Maximale Anzahl von E-Mails, die in einer Stunde gesendet werden sollen. " +"Leer oder 0 bedeutet keine Drosselung. Wenn Ihr Provider eine andere " +"Geschwindigkeitsbegrenzung (Nachrichten/Minute oder Nachrichten/Sekunde) " +"verwendet, dann konvertieren Sie diesen Wert in Nachrichten/Stunde (1m/s => " +"3600m/h). Diese Limite gilt pro Sendungsprozess." + +#: views/settings.hbs:66 +msgid "VERP bounce handling" +msgstr "VERP bounce Handhabung" + +#: 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 ist in der Lage, VERP-basiertes Routing zu verwenden, um Bounces " +"zu erkennen. In diesem Fall wird die Nachricht an den Empfänger über eine " +"benutzerdefinierte VERP-Adresse als Return Path der Nachricht gesendet. Wenn " +"die Nachricht nicht akzeptiert wird, wird eine Bounce-E-Mail an diese " +"spezielle VERP-Adresse gesendet und damit ein Bounce erkannt." + +#: 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 "" +"Damit VERP funktioniert, müssen Sie einen DNS MX Record einrichten, der auf " +"Ihren Mailtrain Hostnamen verweist. Sie müssen auch sicherstellen, dass die " +"Mailtrain VERP-Schnittstelle von Port 25 Ihres Servers verfügbar ist (Port " +"25 benötigt normalerweise Root-Benutzerberechtigung). Folglich landen E-" +"Mails an someuser@verp-hostname auf diesem Server." + +#: 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 "" +"VERP funktioniert in der Regel nur, wenn Sie Ihren eigenen SMTP-Server " +"verwenden. Reguläre Relay Services (SES, SparkPost, Gmail etc.) neigen dazu, " +"die VERP-Adresse aus der Nachricht zu entfernen." + +#: views/settings.hbs:70 +msgid "Use VERP to catch bounces" +msgstr "VERP verwenden um bounces zu erfassen" + +#: views/settings.hbs:71 +msgid "Server hostname" +msgstr "Hostname" + +#: views/settings.hbs:72 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "VERP Server Hostname, z.B. bounces.example.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 "" +"VERP bounce handling Server Hostname. Dieser Hostname wird in der SMTP " +"envelope FROM Adresse verwendet. Die MX DNS Records sollten auf diesen " +"Server verweisen" + +#: views/settings.hbs:74 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" +"VERP Bounce handling Server ist nicht aktiviert. Ändern Sie die Server-" +"Konfigurationsdatei und starten Sie den Server neu, um ihn zu aktivieren." + +#: views/settings.hbs:75 +msgid "GPG Signing" +msgstr "GPG Signierung" + +#: 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 "" +"Nur verschlüsselte Nachrichten können signiert werden. Abonnenten, die " +"keinen GPG Public Key in ihrem Profil hinterlegt haben, erhalten normale E-" +"Mails. Benutzer mit hinterlegtem GPG Key erhalten verschlüsselte Nachrichten " +"und wenn Sie den Signing Key auch gesetzt haben, werden die Nachrichten mit " +"diesem Schlüssel signiert." + +#: views/settings.hbs:77 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" +"Verwenden Sie hier keine sensiblen Keys. Der private Key und Passphrase " +"werden in der Datenbank nicht verschlüsselt." + +#: views/settings.hbs:78 +msgid "Private Key Passphrase" +msgstr "Private Key Passphrase" + +#: views/settings.hbs:79 +msgid "Passphrase for the key if set" +msgstr "Passwort für den Key falls gesetzt" + +#: views/settings.hbs:80 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "" +"Füllen Sie dies nur aus, wenn Ihr privater Schlüssel mit einer Passphrase " +"verschlüsselt ist" + +#: views/settings.hbs:81 +msgid "GPG Private Key" +msgstr "GPG Private Key" + +#: 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 "" +"Dieser Wert ist optional. Wenn Sie keinen private Key zur Verfügung stellen " +"werden GPG verschlüsselte Nachrichten ohne Signierung gesendet." + +#: views/settings.hbs:84 +msgid "DKIM Signing by ZoneMTA" +msgstr "DKIM Signing durch 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 "" +"Wenn Sie ZoneMTA verwenden, kann Mailtrain einen DKIM-Key zur Signierung " +"aller ausgehenden Nachrichten bereitstellen. Andere Dienste bieten in der " +"Regel ihre eigenen Mittel an um Ihre E-mails per DKIM zu signieren" + +#: views/settings.hbs:86 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" +"Verwenden Sie hier keine sensiblen Keys. Der private Key wird in der " +"Datenbank nicht verschlüsselt." + +#: views/settings.hbs:87 +msgid "ZoneMTA DKIM API Key" +msgstr "ZoneMTA DKIM API Key" + +#: views/settings.hbs:88 +msgid "Some secret value" +msgstr "Ein geheimer Wert" + +#: 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 "" +"Geheimer Wert, der ZoneMTA für die Anforderung von DKIM-" +"Schlüsselinformationen bekannt ist. Wenn dieser Wert vom Mailtrain-" +"Installationsskript generiert wurde, können Sie diese Einstellung so " +"behalten, wie sie ist" + +#: views/settings.hbs:90 +msgid "DKIM domain" +msgstr "DKIM Domain" + +#: views/settings.hbs:91 +msgid "Domain name for the DKIM key" +msgstr "Domain-Name für den DKIM-Key" + +#: views/settings.hbs:92 +msgid "Leave blank to use the sender email address domain" +msgstr "Leer lassen, um die Domain der Sender-E-Mail-Adresse zu verwenden" + +#: views/settings.hbs:93 views/settings.hbs:94 +msgid "DKIM key selector" +msgstr "DKIM Key Selector" + +#: views/settings.hbs:95 +msgid "Signing is disabled without a valid selector value" +msgstr "Signierung ist ohne gültigen Wert deaktiviert" + +#: views/settings.hbs:96 +msgid "DKIM Private Key" +msgstr "DKIM Private Key" + +#: views/settings.hbs:98 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" +"Dieser Wert ist optional. Wenn Sie keinen privaten Schlüssel bereitstellen, " +"werden Nachrichten nicht signiert." + +#: views/subscription/mail-confirm-html.mjml.hbs:1 +#: views/subscription/mail-confirm-text.hbs:1 routes/subscription.js:551 +msgid "Please Confirm Subscription" +msgstr "Bitte bestätigen Sie ihr Abonnement" + +#: views/subscription/mail-confirm-html.mjml.hbs:2 +#: views/subscription/mail-confirm-text.hbs:2 +msgid "Yes, subscribe me to this list" +msgstr "Ja, tragen Sie mich in diese Liste ein" + +#: views/subscription/mail-confirm-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 "" +"Wenn Sie diese E-Mail versehentlich erhalten haben, löschen Sie sie einfach. " +"Sie werden nicht abonniert, wenn Sie nicht auf den Bestätigungslink oben " +"klicken." + +#: views/subscription/mail-confirm-html.mjml.hbs:4 +#: views/subscription/mail-confirm-text.hbs:4 +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:8 +#: views/subscription/mail-subscription-confirmed-text.hbs:7 +#: views/subscription/mail-unsubscribe-confirmed-html.mjml.hbs:5 +#: views/subscription/mail-unsubscribe-confirmed-text.hbs:5 +msgid "For questions about this list, please contact:" +msgstr "Bei Fragen zu dieser Liste wenden Sie sich bitte an:" + +#: views/subscription/mail-confirm-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 "" +"Wenn Sie diese E-Mail versehentlich erhalten haben, löschen Sie sie einfach. " +"Sie werden nicht abonniert, es sei denn, Sie klicken auf den " +"Bestätigungslink oben." + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-subscription-confirmed-text.hbs:1 +#: views/subscription/web-subscribed.mjml.hbs:1 +msgid "Subscription Confirmed" +msgstr "Abonnement bestätigt" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed" +msgstr "Ihr Abonnement für unsere Liste wurde bestätigt" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:3 +msgid "If you want to modify your subscription then you can " +msgstr "Wenn Sie Ihr Abonnement ändern möchten, dann können Sie " + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:4 +#: views/subscription/mail-subscription-confirmed-text.hbs:4 +msgid "manage your preferences" +msgstr "Ihre Einstellungen bearbeiten" + +#: 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 "oder" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:6 +#: views/subscription/mail-subscription-confirmed-text.hbs:6 +msgid "unsubscribe here" +msgstr "diesen Newsletter abbestellen" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:7 +#: views/subscription/web-confirm-notice.mjml.hbs:3 +#: views/subscription/web-subscribed.mjml.hbs:4 +#: views/subscription/web-unsubscribe-notice.mjml.hbs:3 +#: views/subscription/web-updated-notice.mjml.hbs:3 +msgid "Return to our website" +msgstr "Zurück zu unserer Website" + +#: views/subscription/mail-subscription-confirmed-text.hbs:2 +#: views/subscription/web-subscribed.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed." +msgstr "Sie wurden erfolgreich in unsere Liste eingetragen." + +#: views/subscription/mail-subscription-confirmed-text.hbs:3 +msgid "If you want to modify your subscription then you can:" +msgstr "Wenn Sie Ihr Abonnement ändern möchten, können Sie:" + +#: views/subscription/mail-unsubscribe-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-unsubscribe-confirmed-text.hbs:1 +msgid "You Are Now Unsubscribed" +msgstr "Sie Sind Nun Abgemeldet" + +#: views/subscription/mail-unsubscribe-confirmed-html.mjml.hbs:2 +msgid "We have removed your email address from our list" +msgstr "Ihre E-Mail-Adresse wurde aus unserer Liste entfernt" + +#: views/subscription/mail-unsubscribe-confirmed-html.mjml.hbs:3 +#: views/subscription/mail-unsubscribe-confirmed-text.hbs:3 +msgid "If you unsubscribed by mistake, you can re-subscribe at:" +msgstr "" +"Wenn Sie sich versehentlich ausgetragen haben, können Sie sich erneut " +"eintragen:" + +#: views/subscription/mail-unsubscribe-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "Wir haben Ihre E-Mail-Adresse aus unserer Liste entfernt." + +#: views/subscription/partials/subscription-custom-fields.hbs:2 +msgid "want to change it?" +msgstr "Möchten Sie diese ändern?" + +#: views/subscription/partials/subscription-custom-fields.hbs:5 +msgid "Download signature verification key" +msgstr "Signature-Verification-Key herunterladen" + +#: views/subscription/partials/subscription-custom-fields.hbs:7 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "" +"Fügen Sie hier Ihren GPG-Public Key ein, um Nachrichten zu verschlüsseln, " +"die an Ihre Adresse gesendet werden" + +#: views/subscription/partials/subscription-custom-fields.hbs:8 +msgid "optional" +msgstr "optional" + +#: views/subscription/partials/subscription-flash-messages.hbs:1 +#: views/subscription/partials/subscription-flash-messages.hbs:3 +msgid "Warning!" +msgstr "Warnung!" + +#: views/subscription/partials/subscription-flash-messages.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "" +"Wenn JavaScript nicht aktiviert war, wurde keine Bestätigungs-E-Mail gesendet" + +#: views/subscription/partials/subscription-flash-messages.hbs:4 +msgid "JavaScript must be enabled in order for this form to work" +msgstr "JavaScript muss aktiviert sein, damit dieses Formular funktioniert" + +#: views/subscription/partials/subscription-manage-address-form.hbs:1 +msgid "Existing Email Address" +msgstr "Aktuelle E-Mail-Adresse" + +#: views/subscription/partials/subscription-manage-address-form.hbs:2 +msgid "New Email Address" +msgstr "Neue E-Mail-Adresse" + +#: views/subscription/partials/subscription-manage-address-form.hbs:3 +msgid "Your new email address" +msgstr "Ihre neue E-Mail-Adresse" + +#: 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 "" +"Sie erhalten eine Bestätigungsanforderung an Ihre neue E-Mail-Adresse, die " +"Sie akzeptieren müssen, bevor Ihre E-Mail tatsächlich geändert wird" + +#: views/subscription/partials/subscription-manage-address-form.hbs:5 +#: views/subscription/web-manage-address.mjml.hbs:2 +msgid "Update Email Address" +msgstr "E-Mail-Adresse aktualisieren" + +#: views/subscription/partials/subscription-manage-form.hbs:1 +#: views/subscription/web-manage.mjml.hbs:2 +msgid "Update Profile" +msgstr "Einstellungen aktualisieren" + +#: 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 "Newsletter abonnieren" + +#: views/subscription/web-confirm-notice.mjml.hbs:1 +#: views/subscription/widget-subscribe.hbs:4 +msgid "Almost Finished" +msgstr "Fast Fertig" + +#: views/subscription/web-confirm-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 "" +"Wir müssen Ihre E-Mail-Adresse bestätigen. Um den Abo-Prozess abzuschließen, " +"klicken Sie bitte auf den Link in der E-Mail, die wir Ihnen gerade geschickt " +"haben." + +#: views/subscription/web-manage-address.mjml.hbs:1 +msgid "Update Your Email Address" +msgstr "E-Mail-Adresse aktualisieren" + +#: views/subscription/web-manage.mjml.hbs:1 +msgid "Update Your Preferences" +msgstr "Einstellungen aktualisieren" + +#: views/subscription/web-subscribe.mjml.hbs:1 +msgid "Subscribe to List" +msgstr "Diese Liste abonnieren" + +#: views/subscription/web-subscribed.mjml.hbs:3 +msgid "Thank you for subscribing!" +msgstr "Danke fürs Abonnieren!" + +#: views/subscription/web-unsubscribe-notice.mjml.hbs:1 +msgid "Unsubscribe Successful" +msgstr "Erfolgreich Deabonniert" + +#: views/subscription/web-unsubscribe-notice.mjml.hbs:2 +msgid "You have been removed from:" +msgstr "Sie wurden entfernt von:" + +#: views/subscription/web-unsubscribe.mjml.hbs:2 +msgid "Enter your email address to unsubscribe from:" +msgstr "Geben Sie Ihre E-Mail-Adresse ein, um sich abzumelden von:" + +#: views/subscription/web-updated-notice.mjml.hbs:1 +msgid "Profile Updated" +msgstr "Einstellungen aktualisiert" + +#: views/subscription/web-updated-notice.mjml.hbs:2 +msgid "Your profile information has been updated." +msgstr "Ihre Profilinformationen wurden aktualisiert." + +#: views/subscription/widget-subscribe.hbs:2 +msgid "Sending ..." +msgstr "Am senden …" + +#: views/subscription/widget-subscribe.hbs:3 +msgid "It looks like you are already subscribed to this list." +msgstr "Es sieht so aus, als hätten Sie diese Liste bereits abonniert." + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "Vorlagen-Name" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "Name für diese Vorlage, z.B. Newsletter" + +#: views/templates/create.hbs:7 +msgid "HTML Editor" +msgstr "HTML Editor" + +#: views/templates/create.hbs:10 views/templates/edit.hbs:9 +msgid "Optional comments about this template" +msgstr "Optionale Kommentare zu dieser Vorlage" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "Zurück zu Vorlagen" + +#: views/templates/edit.hbs:11 +msgid "Duplicate" +msgstr "Duplizieren" + +#: 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 "Automatisierungs-Trigger" + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "Wählen Sie eine Liste für diesen Trigger" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "Trigger-Name" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "Name für diesen Trigger, z.B. Inaktive Abonnenten" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "Optionale Kommentare zu diesem Trigger" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "Trigger-Regel" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "Trigger löst aus" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "Tage nach:" + +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "Abonnement" + +#: views/triggers/create.hbs:16 views/triggers/create.hbs:21 +#: views/triggers/edit.hbs:18 views/triggers/edit.hbs:23 +msgid "Event" +msgstr "Ereignis" + +#: 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 "Kampagne" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "Trigger-Aktion" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "Kampagne senden" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "Trigger bearbeiten" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "Zurück zu Triggers" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "Trigger ist aktiviert" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "Trigger löschen" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "Ausgelöst" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "Triggered Abonnenten" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "Abonnenten, die diesen Trigger ausgelöst haben" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "Auslösungszeit" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "Trigger" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "Ziel-Kampagne" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "Anzahl Auslösungen" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "Dieses Konto wird über LDAP verwaltet." + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "Zugehörige E-Mail-Adresse" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "Ihre E-Mail-Adresse" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you lose your password" +msgstr "" +"Diese Adresse wird für die Wiederherstellung des Kontos verwendet, falls Sie " +"Ihr Passwort vergessen haben" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "Passwortänderung" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "" +"Sie müssen dieses Formular nur ausfüllen, wenn Sie Ihr aktuelles Passwort " +"ändern möchten" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "Aktuelles Passwort" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "Neues Passwort" + +#: views/users/account.hbs:16 views/users/reset.hbs:8 +msgid "Confirm Password" +msgstr "Passwort bestätigen" + +#: views/users/account.hbs:17 views/users/reset.hbs:9 +msgid "Confirm New Password" +msgstr "Neues Passwort bestätigen" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "" +"Sind Sie sicher? Das Zurücksetzen würde das aktuell vorhandene Token " +"ungültig machen." + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "Sind Sie sicher?" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "Access Token zurücksetzen" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "Access Token erstellen" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "Persönlicher Access Token:" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "Access Token noch nicht erstellt" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "Notizen zur 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 "" +"API-Antwort ist eine JSON-Struktur mit error und data properties. Wenn die Antwort error einen Wert hat, ist " +"die Anfrage fehlgeschlagen." + +#: 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 "" +"Sie müssen bei der Anfrage einen korrekten Content-Type " +"definieren. Sie können entweder application/x-www-form-urlencoded für normale Formulardaten oder application/json für eine " +"JSON-Payload verwenden. Nicht unterstützt wird multipart/form-data." + +#: views/users/api.hbs:13 +msgid "Add subscription" +msgstr "Abonnement hinzufügen" + +#: 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 "" +"Dieser API-Aufruf fügt entweder ein neues Abonnement hinzu oder aktualisiert " +"ein bereits vorhandenes Abonnement. Felder, die nicht enthalten sind, " +"bleiben unverändert. Wenn Sie also nur den Wert LAST_NAME aktualisieren, " +"bleibt FIRST_NAME für ein bestehendes Abonnement unverändert." + +#: 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 "Argumente" + +#: 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 "Ihr persönlicher Access Token" + +#: views/users/api.hbs:18 views/users/api.hbs:33 views/users/api.hbs:41 +msgid "subscriber's email address" +msgstr "E-Mail-Adresse des Abonnenten" + +#: 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 "erforderlich" + +#: views/users/api.hbs:20 +msgid "subscriber's first name" +msgstr "Vorname des Abonnenten" + +#: views/users/api.hbs:21 +msgid "subscriber's last name" +msgstr "Nachname des Abonnenten" + +#: views/users/api.hbs:22 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" +"Zeitzone des Abonnenten (zB \"Europa/Berlin“, \"PST\" oder \"UTC\"). Falls " +"nicht gesetzt wird \"UTC\" verwendet." + +#: views/users/api.hbs:23 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" +"Benutzerdefinierter Feldwert. Verwenden Sie Ja/Nein für Optionsgruppenwerte " +"(Checkboxen, Radios-Buttons, Dropdowns)" + +#: views/users/api.hbs:24 +msgid "Additional POST arguments" +msgstr "Weitere POST-Argumente" + +#: 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 "" +"Markieren Sie \"Ja\", wenn Sie sicherstellen möchten, dass die E-Mail als " +"abonniert markiert ist, auch wenn sie zuvor als abgemeldet markiert wurde. " +"Wenn die E-Mail-Adresse bereits abbestellt/gesperrt ist, wird der " +"Abonnementstatus nicht geändert" + +#: 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 "" +"Markieren Sie \"Ja\", wenn Sie eine Bestätigungs-E-Mail an den Abonnenten " +"senden möchten, bevor der Abonnent tatsächlich als abonniert markiert wird" + +#: views/users/api.hbs:28 +msgid "Remove subscription" +msgstr "Abonnement entfernen" + +#: views/users/api.hbs:29 +msgid "This API call marks a subscription as unsubscribed" +msgstr "Dieser API-Aufruf markiert ein Abonnement als abbestellt" + +#: views/users/api.hbs:36 +msgid "Delete subscription" +msgstr "Abonnement löschen" + +#: views/users/api.hbs:37 +msgid "This API call deletes a subscription" +msgstr "Dieser API-Aufruf löscht ein Abonnement" + +#: views/users/api.hbs:44 +msgid "Get list of blacklisted emails" +msgstr "" + +#: views/users/api.hbs:45 +msgid "This API call get list of blacklisted emails." +msgstr "" + +#: views/users/api.hbs:48 +msgid "Start position" +msgstr "Startposition" + +#: views/users/api.hbs:49 +msgid "optional, default 0" +msgstr "optional, standard 0" + +#: views/users/api.hbs:50 +msgid "limit emails count in response" +msgstr "" + +#: views/users/api.hbs:51 +msgid "optional, default 10000" +msgstr "optional, standard 10000" + +#: views/users/api.hbs:52 +msgid "filter by part of email" +msgstr "" + +#: views/users/api.hbs:53 +msgid "optional, default ''" +msgstr "optional, standard ''" + +#: views/users/api.hbs:56 +msgid "This API call either add emails to blacklist" +msgstr "" + +#: views/users/api.hbs:60 views/users/api.hbs:68 +msgid "email address" +msgstr "E-Mail-Adresse" + +#: views/users/api.hbs:63 +msgid "Delete email from blacklist" +msgstr "E-Mail aus der Blacklist löschen" + +#: views/users/api.hbs:64 +msgid "This API call either delete emails from blacklist" +msgstr "" + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "Passwort zurücksetzen" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "Ihr Passwort zurücksetzen?" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "Accounts werden über LDAP verwaltet." + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "Passwort zurücksetzen" + +#: 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 "" +"Bitte geben Sie den Benutzernamen oder die E-Mail-Adresse an, die Sie bei " +"der Anmeldung für Ihr Mailtrain-Konto verwendet haben." + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "" +"Wir senden Ihnen eine E-Mail, mit der Sie Ihr Passwort zurücksetzen können." + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "Benutzername oder E-Mail-Adresse" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "Verifizierung E-Mail senden" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "Angemeldet bleiben" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "Passwort vergessen?" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "Wähle Sie Ihr neues Passwort" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "Bitte geben Sie ein neues Passwort ein." + +#: lib/editor-helpers.js:17 routes/templates.js:95 +msgid "Could not find template with specified ID" +msgstr "Konnte keine Vorlage mit angegebener ID finden" + +#: 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 "Konnte keine Kampagne mit dieser ID finden" + +#: lib/editor-helpers.js:47 routes/editorapi.js:308 +msgid "Invalid resource type" +msgstr "Ungültiger Ressourcentyp" + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "Bad Statuscode %s" + +#: lib/helpers.js:32 +msgid "URL that points to the unsubscribe page" +msgstr "URL, die auf die Abmeldungsseite verweist" + +#: lib/helpers.js:35 +msgid "URL that points to the preferences page of the subscriber" +msgstr "" +"URL, die auf die Persönliche-Einstellungs-Seite des Teilnehmers verweist" + +#: lib/helpers.js:38 +msgid "URL to preview the message in a browser" +msgstr "URL zur Vorschau der E-Mail im Browser" + +#: lib/helpers.js:44 lib/models/segments.js:31 +msgid "First name" +msgstr "Vorname" + +#: lib/helpers.js:47 lib/models/segments.js:35 +msgid "Last name" +msgstr "Nachname" + +#: lib/helpers.js:50 +msgid "Full name (first and last name combined)" +msgstr "Vollständiger Name (Vor- und Nachname kombiniert)" + +#: lib/helpers.js:53 +msgid "Unique ID that identifies the recipient" +msgstr "Eindeutige ID, die den Empfänger identifiziert" + +#: lib/helpers.js:56 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "" +"Eindeutige ID, welche die für diese Kampagne verwendete Liste identifiziert" + +#: lib/helpers.js:59 +msgid "Unique ID that identifies current campaign" +msgstr "Eindeutige ID, welche die aktuelle Kampagne identifiziert" + +#: lib/helpers.js:67 lib/helpers.js:79 +msgid "content from an RSS entry" +msgstr "Inhalt aus einem RSS-Eintrag" + +#: lib/helpers.js:70 +msgid "RSS entry title" +msgstr "" + +#: lib/helpers.js:73 +msgid "RSS entry date" +msgstr "" + +#: lib/helpers.js:76 +msgid "RSS entry link" +msgstr "" + +#: lib/helpers.js:82 +msgid "RSS entry summary" +msgstr "" + +#: lib/helpers.js:85 +msgid "RSS entry image URL" +msgstr "" + +#: lib/mailer.js:245 +msgid "Invalid mail transport" +msgstr "Ungültiger Mail-Transport" + +#: lib/models/campaigns.js:105 lib/models/campaigns.js:132 +#: lib/models/campaigns.js:205 lib/models/campaigns.js:328 +#: lib/models/campaigns.js:589 lib/models/campaigns.js:721 +msgid "Missing Campaign ID" +msgstr "Kampagnen-ID fehlt" + +#: lib/models/campaigns.js:241 +msgid "Emtpy or too large attahcment" +msgstr "Leere oder zu große Anhangsdatei" + +#: lib/models/campaigns.js:407 lib/models/campaigns.js:598 +msgid "Campaign Name must be set" +msgstr "Kampagnenname ist erforderlich" + +#: lib/models/campaigns.js:411 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "RSS URL ist erforderlich und muss eine gültige URL sein" + +#: lib/models/campaigns.js:567 +msgid "Selected template not found" +msgstr "Ausgewählte Vorlage nicht gefunden" + +#: lib/models/campaigns.js:922 +msgid "Invalid or missing message ID" +msgstr "Ungültige oder fehlende Nachrichten-ID" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "Dropdown-Liste" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "Datum (MM/DD/YYY)" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "JSON-Wert für benutzerdefiniertes Rendering" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "Option" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/forms.js:37 lib/models/lists.js:72 lib/models/lists.js:172 +#: lib/models/lists.js:212 lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:74 lib/models/subscriptions.js:574 +#: lib/models/subscriptions.js:637 lib/models/subscriptions.js:823 +#: lib/models/subscriptions.js:926 lib/models/subscriptions.js:980 +#: lib/models/subscriptions.js:1043 lib/models/subscriptions.js:1086 +msgid "Missing List ID" +msgstr "Listen-ID fehlt" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "Ein Optionsfeld erfordert eine ausgewählte Gruppe" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "Feld-ID fehlt" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "Feldname ist erforderlich" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "Feld nicht gefunden" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "Unbekannter Spaltentyp %s" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "Spaltenname fehlt" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "Listen-ID fehlt" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "Die Listen-ID wurde nicht gefunden" + +#: lib/models/forms.js:61 lib/models/forms.js:104 lib/models/forms.js:192 +#: lib/models/forms.js:282 +msgid "Missing Form ID" +msgstr "Formular ID fehlt" + +#: lib/models/forms.js:112 lib/models/forms.js:196 +msgid "Form Name must be set" +msgstr "Formular-Name ist erforderlich" + +#: lib/models/forms.js:298 +msgid "Custom form not found" +msgstr "Formular nicht gefunden" + +#: lib/models/links.js:329 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 "Kampagne nicht gefunden" + +#: lib/models/links.js:337 routes/lists.js:177 services/sender.js:312 +msgid "List not found" +msgstr "Liste nicht gefunden" + +#: lib/models/links.js:345 +msgid "Subscription not found" +msgstr "Abonnement nicht gefunden" + +#: lib/models/lists.js:110 lib/models/lists.js:176 +msgid "List Name must be set" +msgstr "Listennamen ist erforderlich" + +#: lib/models/lists.js:241 +msgid "Missing List CID" +msgstr "Listen CID fehlt" + +#: lib/models/report-templates.js:26 lib/models/report-templates.js:70 +#: lib/models/report-templates.js:142 +msgid "Missing report template ID" +msgstr "Report-Vorlagen-ID fehlt" + +#: lib/models/report-templates.js:77 +msgid "Report template name must be set" +msgstr "Report-Vorlagen-Name ist erforderlich" + +#: lib/models/reports.js:39 lib/models/reports.js:109 lib/models/reports.js:187 +msgid "Missing report ID" +msgstr "Report-ID fehlt" + +#: lib/models/reports.js:115 +msgid "Report name must be set" +msgstr "Report-Name ist erforderlich" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "Anmeldungs-Land" + +#: lib/models/segments.js:19 lib/models/triggers.js:12 +msgid "Sign up date" +msgstr "Anmeldungs-Datum" + +#: lib/models/segments.js:23 lib/models/triggers.js:16 +msgid "Latest open" +msgstr "Letzte Öffnung" + +#: lib/models/segments.js:27 lib/models/triggers.js:20 +msgid "Latest click" +msgstr "Letzter Klick" + +#: 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 "Segment ID fehlt" + +#: lib/models/segments.js:85 lib/models/segments.js:549 +#: lib/models/segments.js:658 +msgid "Segment not found" +msgstr "Segment nicht gefunden" + +#: 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 Tage nach heute" + +#: 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 Tage vor heute" + +#: lib/models/segments.js:148 lib/models/segments.js:410 +msgid "today" +msgstr "heute" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "Ungültiger Segment-Regel-Typ" + +#: 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 "Ausgewähltes Segment nicht gefunden" + +#: 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 "Ungültiger Regel-Typ" + +#: lib/models/segments.js:358 lib/models/segments.js:434 +#: lib/models/segments.js:524 +msgid "Missing Rule ID" +msgstr "Regel-ID fehlt" + +#: lib/models/segments.js:374 +msgid "Specified rule not found" +msgstr "Die Regel wurde nicht gefunden" + +#: lib/models/segments.js:385 +msgid "Specified segment not found" +msgstr "Das Segment wurde nicht gefunden" + +#: lib/models/segments.js:445 +msgid "Selected rule not found" +msgstr "Ausgewählte Regel nicht gefunden" + +#: lib/models/subscriptions.js:148 +msgid "%s: Please Confirm Subscription" +msgstr "%s: Bitte bestätigen Sie Ihr Abonnement" + +#: lib/models/subscriptions.js:258 +msgid "Could not save subscription" +msgstr "Abonnement konnte nicht gespeichert werden" + +#: lib/models/subscriptions.js:441 lib/models/subscriptions.js:471 +msgid "Missing Subscription ID" +msgstr "Abonnement-ID fehlt" + +#: lib/models/subscriptions.js:499 +msgid "Missing Subscription email address" +msgstr "Abonnement E-Mail Adresse fehlt" + +#: lib/models/subscriptions.js:578 lib/models/subscriptions.js:827 +#: lib/models/subscriptions.js:1090 +msgid "Missing subscription ID" +msgstr "Abonnement-ID fehlt" + +#: lib/models/subscriptions.js:641 +msgid "Missing email address" +msgstr "E-Mail-Adresse fehlt" + +#: lib/models/subscriptions.js:930 lib/models/subscriptions.js:984 +#: lib/models/subscriptions.js:1020 +msgid "Missing Import ID" +msgstr "Import-ID fehlt" + +#: lib/models/subscriptions.js:1112 +msgid "Unknown subscription ID" +msgstr "Unbekannte Abonnement-ID" + +#: lib/models/subscriptions.js:1117 +msgid "Nothing seems to be changed" +msgstr "Nichts scheint sich geändert zu haben" + +#: lib/models/subscriptions.js:1131 +msgid "This address is already registered by someone else" +msgstr "Diese Adresse ist bereits von jemand anderem registriert" + +#: lib/models/templates.js:26 lib/models/templates.js:100 +#: lib/models/templates.js:144 +msgid "Missing Template ID" +msgstr "Vorlagen ID fehlt" + +#: lib/models/templates.js:55 lib/models/templates.js:104 +msgid "Template Name must be set" +msgstr "Vorlagen-Name ist erforderlich" + +#: lib/models/triggers.js:29 +msgid "Has Opened" +msgstr "Hat geöffnet" + +#: lib/models/triggers.js:32 +msgid "Has Clicked" +msgstr "Hat geklickt" + +#: lib/models/triggers.js:35 +msgid "Not Opened" +msgstr "Nicht geöffnet" + +#: lib/models/triggers.js:38 +msgid "Not Clicked" +msgstr "Nicht geklickt" + +#: lib/models/triggers.js:175 lib/models/triggers.js:212 +msgid "Missing or invalid list ID" +msgstr "Fehlende oder ungültige Listen ID" + +#: lib/models/triggers.js:179 lib/models/triggers.js:264 +msgid "Days in the past are not allowed" +msgstr "Tage in der Vergangenheit sind nicht erlaubt" + +#: 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 "Fehlende oder ungültige Trigger-Regel" + +#: lib/models/triggers.js:190 lib/models/triggers.js:275 +msgid "Invalid subscription configuration" +msgstr "Ungültige Abonnement-Konfiguration" + +#: lib/models/triggers.js:197 lib/models/triggers.js:282 +msgid "Invalid campaign configuration" +msgstr "Kampagnen Konfiguration ungültig" + +#: lib/models/triggers.js:200 lib/models/triggers.js:285 +msgid "A campaing can not be a target for itself" +msgstr "Eine Kampagne kann kein Ziel für sich selbst sein" + +#: lib/models/triggers.js:233 +msgid "Could not store trigger row" +msgstr "Trigger-Zeile konnte nicht gespeichert werden" + +#: lib/models/triggers.js:250 +msgid "Missing or invalid Trigger ID" +msgstr "Trigger-ID fehlt oder ist ungültig" + +#: lib/models/triggers.js:317 +msgid "Missing Trigger ID" +msgstr "Trigger-ID fehlt" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "Der Benutzer konnte nicht gespeichert werden" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "E-Mail-Adresse ist erforderlich" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "Fehler beim Überprüfen der Benutzerdaten" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" +"E-Mail-Adresse konnte nicht geändert werden, da ein anderer Benutzer mit " +"derselben E-Mail-Adresse bereits existiert" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "Aktuelles Passwort inkorrekt" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "Neues Passwort fehlt" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "Passwörter stimmen nicht überein" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "Benutzer-ID nicht vorhanden" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "Benutzername ist erforderlich" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "Mailer Passwort Änderungsanfrage" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "Fehlender Benutzername oder Reset-Token" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "Neues Passwort ist ungültig" + +#: lib/passport.js:40 +msgid "%s logged out" +msgstr "%s hat sich abgemeldet" + +#: lib/passport.js:53 +msgid "Failed to authenticate user" +msgstr "Der Benutzer konnte nicht authentifiziert werden" + +#: lib/passport.js:69 +msgid "Logged in as %s" +msgstr "Eingeloggt als %s" + +#: lib/passport.js:128 +msgid "Incorrect username or password" +msgstr "Falscher Benutzername oder Passwort" + +#: lib/subscription-mail-helpers.js:39 +msgid "%s: Email Address Already Registered" +msgstr "%s: Email-Adresse bereits registriert" + +#: lib/subscription-mail-helpers.js:49 +msgid "%s: Please Confirm Email Change in Subscription" +msgstr "%s: Bitte bestätigen Sie die Änderung der Email-Adresse" + +#: lib/subscription-mail-helpers.js:69 +msgid "%s: Please Confirm Unsubscription" +msgstr "%s: Bitte bestätigen Sie die Kündigung des Abonnements" + +#: lib/subscription-mail-helpers.js:76 +msgid "%s: Unsubscription Confirmed" +msgstr "%s: Kündigung des Abonnements bestätigt" + +#: lib/tools.js:148 +msgid "Blocked email address \"%s\"" +msgstr "Gesperrte E-Mail-Adresse \"%s\"" + +#: lib/tools.js:157 +msgid "Invalid email address \"%s\"." +msgstr "Ungültige E-Mail-Adresse \"%s\"." + +#: lib/tools.js:160 +msgid "MX record not found for domain" +msgstr "MX-Record für die Domäne nicht gefunden" + +#: lib/tools.js:163 +msgid "Address domain not found" +msgstr "Address-Domain nicht gefunden" + +#: lib/tools.js:166 +msgid "Address domain name is required" +msgstr "Address-Domain-Name ist erforderlich" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:224 +msgid "Not Found" +msgstr "Nicht gefunden" + +#: routes/archive.js:121 services/sender.js:449 +msgid "Received status code %s from %s" +msgstr "Empfangener Statuscode %s von %s" + +#: routes/archive.js:153 routes/campaigns.js:894 +msgid "Attachment not found" +msgstr "Anhangs-Datei nicht gefunden" + +#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:35 +#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:13 +#: 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 "Sie müssen angemeldet sein, um auf geschützte Inhalte zuzugreifen." + +#: routes/campaigns.js:117 +msgid "Could not create campaign" +msgstr "Kampagne konnte nicht erstellt werden" + +#: routes/campaigns.js:120 +msgid "Campaign “%s” created" +msgstr "Die Kampagne “%s” wurde erstellt" + +#: routes/campaigns.js:209 +msgid "Campaign settings updated" +msgstr "Kampagnen-Einstellungen aktualisiert" + +#: routes/campaigns.js:211 +msgid "Campaign settings not updated" +msgstr "Kampagnen-Einstellungen nicht aktualisiert" + +#: routes/campaigns.js:227 routes/campaigns.js:744 +msgid "Campaign deleted" +msgstr "Kampagne gelöscht" + +#: routes/campaigns.js:229 routes/campaigns.js:746 +msgid "Could not delete specified campaign" +msgstr "Die Kampagne konnte nicht gelöscht werden" + +#: routes/campaigns.js:248 +msgid "Idling" +msgstr "Ruhend" + +#: routes/campaigns.js:251 +msgid "Scheduled" +msgstr "Geplant" + +#: routes/campaigns.js:257 +msgid "Paused" +msgstr "Pausiert" + +#: routes/campaigns.js:259 +msgid "Inactive" +msgstr "Inaktiv" + +#: routes/campaigns.js:261 +msgid "Active" +msgstr "Aktiv" + +#: routes/campaigns.js:263 +msgid "Other" +msgstr "Andere" + +#: routes/campaigns.js:421 +msgid "Unknown status selector" +msgstr "Unbekannter Status-Selektor" + +#: routes/campaigns.js:762 +msgid "Scheduled sending" +msgstr "Senden geplant" + +#: routes/campaigns.js:764 +msgid "Could not schedule sending" +msgstr "Versand konnte nicht geplant werden" + +#: routes/campaigns.js:776 +msgid "Sending resumed" +msgstr "Versand wieder aufgenommen" + +#: routes/campaigns.js:778 +msgid "Could not resume sending" +msgstr "Versand konnte nicht fortgesetzt werden" + +#: routes/campaigns.js:790 +msgid "Sending reset" +msgstr "Versand zurückgesetzt" + +#: routes/campaigns.js:792 +msgid "Could not reset sending" +msgstr "Versand konnte nicht zurückgesetzt werden" + +#: routes/campaigns.js:804 routes/campaigns.js:832 +msgid "Sending paused" +msgstr "Versand pausiert" + +#: routes/campaigns.js:806 routes/campaigns.js:834 +msgid "Could not pause sending" +msgstr "Versand konnte nicht pausiert werden" + +#: routes/campaigns.js:818 +msgid "Sending activated" +msgstr "Versand aktiviert" + +#: routes/campaigns.js:820 +msgid "Could not activate sending" +msgstr "Versand konnte nicht aktiviert werden" + +#: routes/campaigns.js:855 +msgid "Attachment uploaded" +msgstr "Anhang hochgeladen" + +#: routes/campaigns.js:857 +msgid "Could not store attachment" +msgstr "Anhang konnte nicht gespeichert werden" + +#: routes/campaigns.js:874 +msgid "Attachment deleted" +msgstr "Anhang gelöscht" + +#: routes/campaigns.js:876 +msgid "Could not delete attachment" +msgstr "Anhang konnte nicht gelöscht werden" + +#: routes/editorapi.js:41 +msgid "Invalid editor name" +msgstr "Ungültiger Editorname" + +#: routes/editorapi.js:237 routes/editorapi.js:275 +msgid "Method not supported" +msgstr "Methode wird nicht unterstützt" + +#: routes/editorapi.js:352 +msgid "Invalid resource type or ID" +msgstr "Ungültiger Ressourcentyp oder ID" + +#: 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 "Ausgewählte Listen ID nicht gefunden" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "Feld konnte nicht erstellt werden" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "Ausgewähltes Feld nicht gefunden" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "Feld Einstellungen aktualisiert" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "Feld Einstellungen nicht aktualisiert" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "Benutzerdefiniertes Feld gelöscht" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "Das angegebene Feld konnte nicht gelöscht werden" + +#: routes/forms.js:78 +msgid "Could not create custom form" +msgstr "Formular konnte nicht erstellt werden" + +#: routes/forms.js:105 +msgid "Selected form not found" +msgstr "Das Formular wurde nicht gefunden" + +#: routes/forms.js:136 +msgid "The plaintext version for this email" +msgstr "Die Textversion für dieses E-Mail" + +#: routes/forms.js:137 +msgid "Custom forms use MJML for formatting" +msgstr "Formulare verwenden MJML zur formatierung" + +#: routes/forms.js:138 +msgid "See the MJML documentation here" +msgstr "" +"Die MJML Dokumentation finden Sie hier" + +#: routes/forms.js:146 +msgid "Layout" +msgstr "Layout" + +#: routes/forms.js:152 +msgid "Form Input Style" +msgstr "Formular Input Style" + +#: routes/forms.js:154 +msgid "" +"This CSS stylesheet defines the appearance of form input elements and alerts" +msgstr "" +"Dieses CSS-Stylesheet definiert das Aussehen von Formulareingabeelementen " +"und Warnungen" + +#: routes/forms.js:160 +msgid "Web - Subscribe" +msgstr "Web - Abonnieren" + +#: routes/forms.js:165 +msgid "Web - Confirm Notice" +msgstr "Web - Bestätigung erforderlich" + +#: routes/forms.js:170 +msgid "Mail - Confirm Subscription (MJML)" +msgstr "Mail - Bestätigung erforderlich (MJML)" + +#: routes/forms.js:175 +msgid "Mail - Confirm Subscription (Text)" +msgstr "Mail - Bestätigung erforderlich (Text)" + +#: routes/forms.js:180 +msgid "Web - Subscribed Notice" +msgstr "Web - Notiz: Abonnement bestätigt" + +#: routes/forms.js:185 +msgid "Mail - Subscription Confirmed (MJML)" +msgstr "Mail - Abonnement bestätigt (MJML)" + +#: routes/forms.js:190 +msgid "Mail - Subscription Confirmed (Text)" +msgstr "Mail - Abonnement bestätigt (Text)" + +#: routes/forms.js:198 +msgid "Web - Manage Preferences" +msgstr "Web - Profil bearbeiten" + +#: routes/forms.js:203 +msgid "Web - Manage Address" +msgstr "Web - E-Mail-Adresse bearbeiten" + +#: routes/forms.js:208 +msgid "Web - Updated Notice" +msgstr "Web - Aktualisiert" + +#: routes/forms.js:216 +msgid "Web - Unsubscribe" +msgstr "Web - Abmelden" + +#: routes/forms.js:221 +msgid "Web - Unsubscribe Notice" +msgstr "Web - Abmelden" + +#: routes/forms.js:226 +msgid "Mail - Unsubscribe Confirmed (MJML)" +msgstr "Mail - Abmeldung bestätigt (MJML)" + +#: routes/forms.js:231 +msgid "Mail - Unsubscribe Confirmed (Text)" +msgstr "Mail - Abmeldung bestätigt (Text)" + +#: routes/forms.js:269 +msgid "Form settings updated" +msgstr "Formular-Einstellungen aktualisiert" + +#: routes/forms.js:271 +msgid "Form settings not updated" +msgstr "Formular-Einstellungen nicht aktualisiert" + +#: routes/forms.js:287 +msgid "Custom form deleted" +msgstr "Formular wurde gelöscht" + +#: routes/forms.js:289 +msgid "Could not delete specified form" +msgstr "Das Formular konnte nicht gelöscht werden" + +#: routes/index.js:11 +msgid "Self Hosted Newsletter App" +msgstr "Selbst gehostete Newsletter-App" + +#: routes/links.js:39 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "" +"Ups, wir konnten keinen Link für die URL finden, die Sie geklickt haben" + +#: routes/lists.js:80 +msgid "Could not create list" +msgstr "Die Liste konnte nicht erstellt werden" + +#: routes/lists.js:83 +msgid "List created" +msgstr "Liste erstellt" + +#: routes/lists.js:91 routes/lists.js:267 routes/lists.js:332 +#: routes/lists.js:371 routes/lists.js:440 routes/lists.js:465 +#: routes/lists.js:510 routes/lists.js:532 routes/lists.js:561 +#: routes/lists.js:640 routes/lists.js:697 routes/lists.js:724 +msgid "Could not find list with specified ID" +msgstr "Die Liste mit angegebener ID konnte nicht gefunden werden" + +#: routes/lists.js:118 +msgid "List settings updated" +msgstr "Listeneinstellungen aktualisiert" + +#: routes/lists.js:120 +msgid "List settings not updated" +msgstr "Listeneinstellungen nicht aktualisiert" + +#: routes/lists.js:138 +msgid "List deleted" +msgstr "Liste gelöscht" + +#: routes/lists.js:140 +msgid "Could not delete specified list" +msgstr "Die Liste konnte nicht gelöscht werden" + +#: routes/lists.js:202 +msgid "Unknown" +msgstr "Unbekannt" + +#: routes/lists.js:202 +msgid "Complained" +msgstr "Beschwert" + +#: routes/lists.js:233 +msgid "Invalid key" +msgstr "Ungültiger Key" + +#: routes/lists.js:235 +msgid "Expired key" +msgstr "Abgelaufener Key" + +#: routes/lists.js:237 +msgid "Revoked key" +msgstr "Widerrufener Key" + +#: routes/lists.js:287 +msgid "Initializing" +msgstr "Initialisierung" + +#: routes/lists.js:290 +msgid "Initialized" +msgstr "Initialisiert" + +#: routes/lists.js:293 +msgid "Importing" +msgstr "Importieren" + +#: routes/lists.js:299 +msgid "Errored" +msgstr "Fehlerhaft" + +#: routes/lists.js:377 routes/lists.js:446 routes/lists.js:471 +msgid "Could not find subscriber with specified ID" +msgstr "Der Abonnent mit angegebenen ID konnte nicht gefunden werden" + +#: routes/lists.js:423 +msgid "Could not add subscription" +msgstr "Das Abonnement konnte nicht hinzugefügt werden" + +#: routes/lists.js:428 +msgid "%s was successfully added to your list" +msgstr "%s wurde Ihrer Liste erfolgreich hinzugefügt" + +#: routes/lists.js:430 +msgid "%s was not added to your list" +msgstr "%s wurde nicht zu Ihrer Liste hinzugefügt" + +#: routes/lists.js:452 +msgid "Could not unsubscribe user" +msgstr "Der Benutzer konnte nicht deabonniert werden" + +#: routes/lists.js:455 +msgid "%s was successfully unsubscribed from your list" +msgstr "%s wurde erfolgreich von Ihrer Liste entfernt" + +#: routes/lists.js:475 +msgid "%s was successfully removed from your list" +msgstr "%s wurde erfolgreich aus Ihrer Liste entfernt" + +#: routes/lists.js:487 +msgid "Another subscriber with email address %s already exists" +msgstr "Ein anderer Abonnent mit der E-Mail-Adresse %s existiert bereits" + +#: routes/lists.js:494 +msgid "Subscription settings updated" +msgstr "Abonnementeinstellungen aktualisiert" + +#: routes/lists.js:496 +msgid "Subscription settings not updated" +msgstr "Abonnementeinstellungen nicht aktualisiert" + +#: routes/lists.js:538 routes/lists.js:646 routes/lists.js:682 +#: routes/lists.js:710 routes/lists.js:730 +msgid "Could not find import data with specified ID" +msgstr "Keine Importdaten für diese ID gefunden" + +#: routes/lists.js:569 +msgid "Could not process CSV" +msgstr "CSV-Datei konnte nicht verarbeitet werden" + +#: routes/lists.js:578 +msgid "Could not create importer" +msgstr "Importer konnte nicht erstellt werden" + +#: routes/lists.js:629 +msgid "Empty file" +msgstr "Leere Datei" + +#: routes/lists.js:686 +msgid "Import started" +msgstr "Import gestartet" + +#: routes/lists.js:714 +msgid "Import restarted" +msgstr "Import neu gestartet" + +#: routes/lists.js:797 +msgid "One-step (i.e. no email with confirmation link)" +msgstr "Ein Schritt ohne Formular (direkt nach dem Klicken auf den Link erfolgt die Abmeldung)" + +#: routes/lists.js:803 +msgid "" +"One-step with unsubscription form (i.e. no email with confirmation link)" +msgstr "" +"Ein Schritt mit Formular (nach dem Klick wird ein Formular mit der E-Mail Adresse angezeigt)" + +#: routes/lists.js:809 +msgid "Two-step (i.e. an email with confirmation link will be sent)" +msgstr "Zwei Schritte ohne Formular (direkt nach dem Klick auf den Link wird eine E-Mail mit Bestätigungslink versendet)" + +#: routes/lists.js:815 +msgid "" +"Two-step with unsubscription form (i.e. an email with confirmation link will " +"be sent)" +msgstr "" +"Zwei Schritte mit Formular (nach dem Klick wird ein Formular mit der E-Mail Adresse " "angezeigt, dann wird eine E-Mail mit Bestätigungslink versendet)" + +#: routes/lists.js:821 +msgid "" +"Manual (i.e. unsubscription has to be performed by the list administrator)" +msgstr "" +"Manuell (die Abmeldung muss durch den Listen Admin vorgenommen werden)" + +#: routes/report-templates.js:246 +msgid "Could not create report template" +msgstr "Report-Vorlage konnte nicht erstellt werden" + +#: routes/report-templates.js:249 +msgid "Report template “%s” created" +msgstr "Die Report-Vorlage “%s” wurde erstellt" + +#: routes/report-templates.js:257 +msgid "Could not find report template with specified ID" +msgstr "Report-Vorlage mit angegebener ID konnte nicht gefunden werden" + +#: routes/report-templates.js:280 +msgid "Report template updated" +msgstr "Report-Vorlage aktualisiert" + +#: routes/report-templates.js:282 +msgid "Report template not updated" +msgstr "Report-Vorlage wurde nicht aktualisiert" + +#: routes/report-templates.js:298 +msgid "Report template deleted" +msgstr "Report-Vorlage gelöscht" + +#: routes/report-templates.js:300 +msgid "Could not delete specified report template" +msgstr "Die Report-Vorlage konnte nicht gelöscht werden" + +#: routes/reports.js:124 routes/reports.js:130 +msgid "Could not create report" +msgstr "Der Report konnte nicht erstellt werden" + +#: routes/reports.js:135 +msgid "Report “%s” created" +msgstr "Report “%s” erstellt" + +#: 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 "Der Report mit dieser ID konnte nicht gefunden werden" + +#: routes/reports.js:188 routes/reports.js:194 +msgid "Could not update report" +msgstr "Der Report konnte nicht aktualisiert werden" + +#: routes/reports.js:197 +msgid "Report updated" +msgstr "Report aktualisiert" + +#: routes/reports.js:199 +msgid "Report not updated" +msgstr "Report nicht aktualisiert" + +#: routes/reports.js:212 +msgid "Report deleted" +msgstr "Report gelöscht" + +#: routes/reports.js:214 +msgid "Could not delete specified report" +msgstr "Der Report konnte nicht gelöscht werden" + +#: routes/reports.js:230 +msgid "Could not find report template" +msgstr "Report-Vorlage konnte nicht gefunden werden" + +#: routes/reports.js:260 +msgid "Unknown type of template" +msgstr "Unbekannter Mime-Type des Template" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "Segment konnte nicht erstellt werden" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "Segment erstellt" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "Ausgewählte Segment-ID wurde nicht gefunden" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "Segmenteinstellungen aktualisiert" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "Segmenteinstellungen nicht aktualisiert" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "Segment gelöscht" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "Das angegebene Segment konnte nicht gelöscht werden" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "Regel konnte nicht erstellt werden" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "Regel erstellt" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "Regeleinstellungen aktualisiert" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "Regeleinstellungen nicht aktualisiert" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "Regel gelöscht" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "Die Regel konnte nicht gelöscht werden" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "TLS verwenden" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "Normalerweise ausgewählt für Port 465" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "STARTTLS verwenden" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "normalerweise ausgewählt für Port 587 und 25" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "Keine Verschlüsselung verwenden" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "Einstellungen aktualisiert" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "Ungültiger Mail Transport Type" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "Ungültiger Access Key" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "Ungültige AWS Zugangsdaten" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "Verbindung abgelehnt, überprüfen Sie Hostname und Port." + +#: 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 "" +"Keine Grußnachricht vom Server erhalten. Dies kann bei der Verbindung zu " +"einem TLS-Port ohne TLS auftreten." + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "Keine Grußnachricht vom Server erhalten." + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" +"Zeitüberschreitung der Verbindung. Überprüfen Sie Ihre Firewall-" +"Einstellungen, der Ziel-Port ist wahrscheinlich blockiert." + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "" +"Authentifizierung nicht akzeptiert, Server erwartet die Verwendung von " +"STARTTLS." + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "" +"Authentifizierung fehlgeschlagen, Benutzername und Passwort überprüfen." + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "Mailer Überprüfung fehlgeschlagen." + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "Server antwortete mit: \"%s\"" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "Mailer-Einstellungen überprüft, bereit zum Senden!" + +#: routes/subscription.js:32 +msgid "Not allowed by CORS" +msgstr "Nicht erlaubt von CORS" + +#: routes/subscription.js:50 +msgid "Selected subscription not found" +msgstr "Ausgewähltes Abonnement nicht gefunden" + +#: routes/subscription.js:60 routes/subscription.js:181 +#: routes/subscription.js:266 routes/subscription.js:324 +#: routes/subscription.js:377 routes/subscription.js:429 +#: routes/subscription.js:511 routes/subscription.js:562 +#: routes/subscription.js:638 routes/subscription.js:660 +#: routes/subscription.js:720 routes/subscription.js:744 +#: routes/subscription.js:809 +msgid "Selected list not found" +msgstr "Ausgewählte Liste nicht gefunden" + +#: routes/subscription.js:134 +msgid "%s: Subscription Confirmed" +msgstr "%s: Abonnement bestätigt" + +#: routes/subscription.js:184 routes/subscription.js:514 +msgid "The list does not allow public subscriptions." +msgstr "Die Liste erlaubt keine öffentlichen Abonnements." + +#: routes/subscription.js:493 routes/subscription.js:495 +msgid "Email address not set" +msgstr "E-Mail-Adresse nicht gesetzt" + +#: routes/subscription.js:538 +msgid "Could not store confirmation data" +msgstr "Die Bestätigungsdaten konnten nicht gespeichert werden" + +#: routes/subscription.js:576 routes/subscription.js:675 +#: routes/subscription.js:759 +msgid "Subscription not found from this list" +msgstr "Abonnement konnte in dieser Liste nicht gefunden werden" + +#: routes/subscription.js:735 +msgid "Email address updated, check your mailbox for verification instructions" +msgstr "" +"Die E-Mail-Adresse wurde aktualisiert. Bitte überprüfen Sie Ihre Mailbox zur " +"Bestätigung" + +#: routes/subscription.js:858 +msgid "%s: Unsubscribe Confirmed" +msgstr "%s: Abmeldungen Bestätigt" + +#: routes/subscription.js:905 routes/subscription.js:921 +msgid "Public key is not set" +msgstr "Public-Key ist nicht gesetzt" + +#: routes/templates.js:84 +msgid "Could not create template" +msgstr "Vorlage konnte nicht erstellt werden" + +#: routes/templates.js:87 +msgid "Template created" +msgstr "Vorlage erstellt" + +#: routes/templates.js:126 +msgid "Template settings updated" +msgstr "Vorlageneinstellungen aktualisiert" + +#: routes/templates.js:128 +msgid "Template settings not updated" +msgstr "Template Einstellungen wurden nicht aktualisiert" + +#: routes/templates.js:144 +msgid "Template deleted" +msgstr "Vorlage gelöscht" + +#: routes/templates.js:146 +msgid "Could not delete specified template" +msgstr "Die Vorlage konnte nicht gelöscht werden" + +#: routes/triggers.js:62 routes/triggers.js:79 routes/triggers.js:154 +msgid "Could not find selected list" +msgstr "Die ausgewählte Liste konnte nicht gefunden werden" + +#: routes/triggers.js:131 +msgid "Could not create trigger" +msgstr "Trigger konnte nicht erstellt werden" + +#: routes/triggers.js:138 +msgid "Trigger “%s” created" +msgstr "Trigger “%s” erstellt" + +#: routes/triggers.js:214 +msgid "Trigger settings updated" +msgstr "Trigger-Einstellungen aktualisiert" + +#: routes/triggers.js:216 +msgid "Trigger settings not updated" +msgstr "Trigger-Einstellungen nicht aktualisiert" + +#: routes/triggers.js:228 +msgid "Trigger deleted" +msgstr "Trigger gelöscht" + +#: routes/triggers.js:230 +msgid "Could not delete specified trigger" +msgstr "Der Trigger konnte nicht gelöscht werden" + +#: routes/triggers.js:242 +msgid "Could not find trigger with specified ID" +msgstr "Der Trigger mit dieser ID konnte nicht gefunden werden" + +#: routes/triggers.js:255 +msgid "Trigger not found" +msgstr "Trigger nicht gefunden" + +#: 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 "" +"Eine E-Mail mit Passwort-Reset-Anweisungen wurde an Ihre E-Mail-Adresse " +"gesendet, wenn die E-Mail-Adresse auf unserem System existiert." + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "Unbekanntes oder abgelaufenes Reset-Token" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "Ihr Passwort wurde erfolgreich geändert" + +#: routes/users.js:87 +msgid "User data not found" +msgstr "Benutzerdaten nicht gefunden" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "Access Token aktualisiert" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "Access Token nicht aktualisiert" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "Account-Informationen aktualisiert" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "Account-Informationen nicht aktualisiert" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "Feed Fehler: %s" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "Fand %s neue Kampagnen-Nachrichten im Feed" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "Im Feed wurde nichts neues gefunden" + +#: services/feedcheck.js:146 +msgid "RSS entry %s" +msgstr "RSS Eintrag %s" + +#: services/importer.js:249 +msgid "Could not access import file" +msgstr "Auf die Importdatei konnte nicht zugegriffen werden" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "Unbekannter Trigger-Typ %s" + +#~ msgid "Add new custom field" +#~ msgstr "Neues benutzerdefiniertes Feld hinzufügen" + +#~ msgid "field name" +#~ msgstr "Feldname" + +#~ msgid "one of the following types:" +#~ msgstr "Einer der folgenden Typen:" + +#~ msgid "" +#~ "If the type is 'option' then you also need to specify the parent element " +#~ "ID" +#~ msgstr "" +#~ "Wenn der Typ 'Option' ist, dann müssen Sie auch die übergeordnete Element-" +#~ "ID angeben" + +#~ msgid "" +#~ "Template for the group element. If not set, then values of the elements " +#~ "are joined with commas" +#~ msgstr "" +#~ "Vorlage für das Gruppenelement. Wenn nicht gesetzt, dann werden die Werte " +#~ "der Elemente mit Kommas verbunden" + +#~ msgid "" +#~ "if not visible then the subscriber can not view or modify this value at " +#~ "the profile page" +#~ msgstr "" +#~ "Wenn nicht sichtbar, kann der Abonnent diesen Wert auf der Profilseite " +#~ "weder sehen noch bearbeiten" + +#~ msgid "You are now unsubscribed" +#~ msgstr "Sie sind jetzt aus dieser Liste ausgetragen" + +#~ msgid "List management" +#~ msgstr "Listenverwaltung" + +#~ msgid "Custom fields" +#~ msgstr "Benutzerdefinierte Felder" + +#~ msgid "List segmentation" +#~ msgstr "Listensegmentierung" + +#~ msgid "Donate to author" +#~ msgstr "Spenden willkommen" + +#~ msgid "or donate using PayPal" +#~ msgstr "Oder spenden Sie mit PayPal" + +#~ msgid "Click stats" +#~ msgstr "Klick Statistiken" + +#~ msgid "Open source" +#~ msgstr "Open source" + +#~ msgid "Send via any provider" +#~ msgstr "Senden Sie über jeden Anbieter" + +#~ msgid "Trigger based automation" +#~ msgstr "Triggerbasierte Automatisierung" + +#~ msgid "Self hosted newsletter app built on top of Nodemailer" +#~ msgstr "Selbst gehostete Newsletter-App basierend auf Nodemailer" + +#~ msgid "Subscribe to our newsletter" +#~ msgstr "Abonnieren Sie unseren Newsletter" + +#~ msgid "To extract the text from HTML click" +#~ msgstr "Um den Klartext aus dem HTML Dokument zu extrahieren, klicken Sie" + +#~ msgid "Almost finished." +#~ msgstr "Fast fertig." + +#~ msgid "return to our website" +#~ msgstr "zurück zu unserer Webseite" + +#~ msgid "Update your Email Address" +#~ msgstr "E-Mail-Adresse aktualisieren" + +#~ msgid "Update your preferences" +#~ msgstr "Einstellungen aktualisieren" + +#~ msgid "" +#~ "JavaScript must be enabled in order for the subscription form to work" +#~ msgstr "" +#~ "JavaScript muss aktiviert sein, damit das Anmeldeformular funktionieren " +#~ "kann" + +#~ msgid "continue to our website" +#~ msgstr "weiter zu unserer Webseite" + +#~ 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 " +#~ "15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM" +#~ msgstr "" +#~ "Loren Ipsum 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 " +#~ "15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM" + +#~ msgid "" +#~ "We have received a password change request for your Mailtrain account" +#~ msgstr "" +#~ "Wir haben eine Passwortänderungsanfrage für Ihr Mailtrain-Konto erhalten" + +#~ msgid "Reset password:" +#~ msgstr "Passwort zurücksetzen:" + +#~ msgid "(optional)" +#~ msgstr "(optional)" + +#~ msgid "–– Select ––" +#~ msgstr "— Bitte wählen —" + +#~ msgid "You have been removed from" +#~ msgstr "Sie wurden aus dieser Liste ausgetragen:" + +#~ msgid "Enter your email address to unsubscribe from" +#~ msgstr "Geben Sie Ihre E-Mail-Adresse ein, um sich abzumelden von" + +#~ msgid "Yes, subscribe me to this list:" +#~ msgstr "Ja, tragen Sie mich in diese Liste ein:" + +#: 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 "Das Abonnement wurde in dieser Liste nicht gefunden" + +#: 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 "" +"Wir müssen Ihre E-Mail-Adresse bestätigen. Um die Kündigung Ihres Abonnements abzuschließen, " +"klicken Sie bitte auf den Link in der E-Mail, die wir Ihnen gerade geschickt " +"haben." + +msgid "Please Confirm Unsubscription" +msgstr "Bitte bestätigen Sie die Kündigung Ihres Abonnements" + +msgid "Yes, unsubscribe me from this list" +msgstr "Ja, ich möchte dieses Abonnement kündigen" + +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 "" +"Wenn Sie diese E-Mail versehentlich erhalten haben, löschen Sie sie einfach. " +"Ihr Abonnement wird nicht gekündigt, wenn Sie nicht auf den Bestätigungslink oben klicken." diff --git a/languages/es_ES.mo b/languages/es_ES.mo new file mode 100644 index 00000000..214acc8a Binary files /dev/null and b/languages/es_ES.mo differ diff --git a/languages/es_ES.po b/languages/es_ES.po new file mode 100644 index 00000000..8abe4590 --- /dev/null +++ b/languages/es_ES.po @@ -0,0 +1,4664 @@ +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 lose 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 lib/models/subscriptions.js:391 +msgid "Missing Subscription ID" +msgstr "Falta el ID de Suscripción" + +#: lib/models/subscriptions.js:312 +msgid "Missing Subscription email address" +msgstr "Falta el correo electrónico 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" diff --git a/languages/et.mo b/languages/et.mo new file mode 100644 index 00000000..18e5f344 Binary files /dev/null and b/languages/et.mo differ diff --git a/languages/et.po b/languages/et.po new file mode 100644 index 00000000..88ad5bdb --- /dev/null +++ b/languages/et.po @@ -0,0 +1,3551 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-03-07 21:44+0200\n" +"PO-Revision-Date: 2017-03-08 20:25+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.12\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: views/archive/layout.hbs:1 views/layout.hbs:1 +#: views/subscription/layout.hbs:1 routes/index.js:11 +msgid "Self hosted email newsletter app" +msgstr "Enda majutatud e-posti uudiskirjade rakendus" + +#: 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/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/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 "Esileht" + +#: 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:119 routes/campaigns.js:35 +msgid "Campaigns" +msgstr "Kampaaniad" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Tagasipõrgete info" + +#: 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 "Vaata kampaaniat" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Liikmed, kes põrkusid tagasi:" + +#: views/campaigns/bounced.hbs:7 views/campaigns/clicked.hbs:13 +#: views/campaigns/complained.hbs:7 views/campaigns/delivered.hbs:7 +#: views/campaigns/opened.hbs:7 views/campaigns/unsubscribed.hbs:7 +#: views/lists/subscription/import-failed.hbs:9 views/lists/view.hbs:18 +#: views/triggers/triggered.hbs:6 +msgid "Address" +msgstr "Aadress" + +#: views/campaigns/bounced.hbs:8 views/campaigns/clicked.hbs:14 +#: views/campaigns/complained.hbs:8 views/campaigns/delivered.hbs:8 +#: views/campaigns/opened.hbs:8 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:19 +#: views/subscription/manage.hbs:4 views/subscription/subscribe.hbs:4 +#: views/triggers/triggered.hbs:7 +msgid "First Name" +msgstr "Eesnimi" + +#: views/campaigns/bounced.hbs:9 views/campaigns/clicked.hbs:15 +#: views/campaigns/complained.hbs:9 views/campaigns/delivered.hbs:9 +#: views/campaigns/opened.hbs:9 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:20 +#: views/subscription/manage.hbs:5 views/subscription/subscribe.hbs:5 +#: views/triggers/triggered.hbs:8 +msgid "Last Name" +msgstr "Perekonnanimi" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "SMTP vastus" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Tagasipõrke aeg" + +#: views/campaigns/campaigns.hbs:3 views/campaigns/create-triggered.hbs:24 +#: views/campaigns/create.hbs:3 views/campaigns/create.hbs:4 +#: views/campaigns/create.hbs:27 +msgid "Create Campaign" +msgstr "Loo kampaania" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Tavaline kampaania" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "RSS kampaania" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "Automaatkampaania" + +#: 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:71 +#: views/lists/create.hbs:5 views/lists/edit.hbs:6 +#: views/lists/fields/fields.hbs:6 views/lists/lists.hbs:5 +#: views/lists/segments/segments.hbs:6 views/templates/templates.hbs:5 +#: views/triggers/triggers.hbs:5 +msgid "Name" +msgstr "Nimi" + +#: 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:72 +#: views/lists/create.hbs:7 views/lists/edit.hbs:10 views/lists/lists.hbs:8 +#: views/partials/merge-tag-reference.hbs:4 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 "Kirjeldus" + +#: views/campaigns/campaigns.hbs:10 views/campaigns/view.hbs:73 +#: views/lists/view.hbs:21 views/lists/view.hbs:29 +#: views/triggers/triggers.hbs:6 +msgid "Status" +msgstr "Staatus" + +#: views/campaigns/campaigns.hbs:11 views/campaigns/view.hbs:74 +#: views/lists/view.hbs:22 views/lists/view.hbs:23 +msgid "Created" +msgstr "Loodud" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Lingi info" + +#: views/campaigns/clicked.hbs:6 views/campaigns/view.hbs:61 +msgid "URL" +msgstr "URL" + +#: views/campaigns/clicked.hbs:7 views/campaigns/view.hbs:62 +msgid "Clicks" +msgstr "Klikke" + +#: views/campaigns/clicked.hbs:8 views/campaigns/view.hbs:63 +msgid "% of clicks" +msgstr "% klikkidest" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:64 +msgid "% of messages" +msgstr "% kirjadest" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:67 +msgid "Aggregated clicks" +msgstr "Agregeeritud klikid" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Liikmes, kes klikkisid lingile" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Liikmed, kes klikkisid sellele lingile" + +#: views/campaigns/clicked.hbs:16 +msgid "First click time" +msgstr "Esimese kliki aeg" + +#: views/campaigns/clicked.hbs:17 +msgid "Click count" +msgstr "Klikkide arv" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Kaebuste info" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Liikmed, kes kaebasid ja kes eemaldati listist:" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Kaebuse aeg" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:20 +msgid "Create RSS Campaign" +msgstr "Loo RSS kampaania" + +#: 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 "" +"RSS kampaania seab üles träkkeri RSS aadressi pihta. Kui sellest leitakse " +"uus postitus, siis see saadetakse valitud listile uue kirjana" + +#: 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 "Kampaania nimi" + +#: 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/templates/create.hbs:11 views/templates/edit.hbs:10 +#: views/triggers/create.hbs:9 views/triggers/edit.hbs:10 +msgid "HTML is allowed" +msgstr "HTML kasutamine on lubatud" + +#: 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 "List" + +#: views/campaigns/create-rss.hbs:11 views/campaigns/create-triggered.hbs:10 +#: views/campaigns/create-triggered.hbs:13 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/subscription/manage.hbs:10 views/subscription/subscribe.hbs:10 +#: 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 "Vali" + +#: 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 "liiget" + +#: views/campaigns/create-rss.hbs:13 views/campaigns/edit-rss.hbs:15 +msgid "RSS Feed Url" +msgstr "RSS voo aadress" + +#: 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 "" +"Uued postitused sellest RSS voost saadetakse listi liikmete uue kirjana" + +#: views/campaigns/create-rss.hbs:15 views/campaigns/create-triggered.hbs:17 +#: 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 "E-posti “saatja nimi”" + +#: views/campaigns/create-rss.hbs:16 views/campaigns/create-triggered.hbs:18 +#: views/campaigns/create.hbs:19 views/campaigns/edit-rss.hbs:19 +#: views/campaigns/edit.hbs:18 views/settings.hbs:23 +msgid "This is the name your emails will come from" +msgstr "Seda nime näidatakse kirja saatja nimena" + +#: views/campaigns/create-rss.hbs:17 views/campaigns/create-triggered.hbs:19 +#: 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 "E-posti “saatja” aadress" + +#: views/campaigns/create-rss.hbs:18 views/campaigns/create-triggered.hbs:20 +#: 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 "Sellele aadressile saadavad inimesed vastuseid" + +#: views/campaigns/create-rss.hbs:19 views/campaigns/create-triggered.hbs:23 +#: views/campaigns/create.hbs:26 views/campaigns/edit-rss.hbs:22 +#: views/campaigns/edit-triggered.hbs:22 views/campaigns/edit.hbs:25 +msgid "Disable clicked/opened tracking" +msgstr "Lülita välja klikkide ja avamiste träkkimine" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "Loo automaatkampaania" + +#: 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/templates/create.hbs:13 +msgid "Template" +msgstr "Templiit" + +#: views/campaigns/create-triggered.hbs:14 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "Templiidi valimine tekitab sellest kampaania jaoks eraldi koopia" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "Või alternatiivselt kasuta veebiaadressi kirja sisu jaoks" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:17 +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:28 +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 "" +"Kirja saatmiselt tehakse selle aadressi pihta POST päring koos " +"täiteväljadega. Kasuta seda võimalust, kui soovid HTML sisu genereerida " +"omaenda skriptiga" + +#: views/campaigns/create-triggered.hbs:21 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 "" + +#: views/campaigns/create-triggered.hbs:22 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 "" + +#: views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "" + +#: 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 "" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/campaigns/edit-rss.hbs:23 views/campaigns/edit-triggered.hbs:26 +#: views/campaigns/edit.hbs:34 +msgid "Delete Campaign" +msgstr "" + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:35 views/lists/edit.hbs:13 +#: views/lists/fields/edit.hbs:39 views/lists/segments/edit.hbs:14 +#: views/lists/segments/rule-edit.hbs:38 views/lists/subscription/edit.hbs:17 +#: views/settings.hbs:99 views/templates/edit.hbs:12 views/triggers/edit.hbs:30 +#: views/users/account.hbs:18 +msgid "Update" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +msgid "General" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:17 +msgid "his is the name your emails will come from" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:23 views/campaigns/edit.hbs:26 +msgid "Template Settings" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:27 +msgid "Template URL" +msgstr "" + +#: 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 "" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:29 +msgid "Attachments" +msgstr "" + +#: views/campaigns/edit.hbs:30 +msgid "File" +msgstr "" + +#: views/campaigns/edit.hbs:31 +msgid "Size" +msgstr "" + +#: views/campaigns/edit.hbs:32 views/campaigns/view.hbs:66 +#: views/lists/fields/fields.hbs:12 views/lists/view.hbs:32 +msgid "No data available in table" +msgstr "" + +#: views/campaigns/edit.hbs:33 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "" + +#: views/campaigns/opened.hbs:10 +msgid "First open" +msgstr "" + +#: views/campaigns/opened.hbs:11 +msgid "Opened count" +msgstr "" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:26 +#: views/lists/subscription/import.hbs:10 routes/lists.js:171 +msgid "Unsubscribed" +msgstr "" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:25 +msgid "Delivered" +msgstr "" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "" + +#: views/campaigns/view.hbs:22 routes/lists.js:171 +msgid "Bounced" +msgstr "" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who bounced" +msgstr "" + +#: views/campaigns/view.hbs:24 +msgid "Complaints" +msgstr "" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who complained for this message" +msgstr "" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who unsubscribed after this message" +msgstr "" + +#: views/campaigns/view.hbs:28 +msgid "Opened" +msgstr "" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who opened this message" +msgstr "" + +#: views/campaigns/view.hbs:30 +msgid "Clicked" +msgstr "" + +#: views/campaigns/view.hbs:31 views/campaigns/view.hbs:68 +msgid "List subscribers who clicked on a link" +msgstr "" + +#: views/campaigns/view.hbs:32 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" + +#: views/campaigns/view.hbs:33 +msgid "Delay sending" +msgstr "" + +#: views/campaigns/view.hbs:34 +msgid "hours" +msgstr "" + +#: views/campaigns/view.hbs:35 +msgid "minutes" +msgstr "" + +#: views/campaigns/view.hbs:36 +msgid "Send to subscribers:" +msgstr "" + +#: views/campaigns/view.hbs:37 +msgid "Are you sure? This action would reset scheduling" +msgstr "" + +#: views/campaigns/view.hbs:38 +msgid "Cancel" +msgstr "" + +#: views/campaigns/view.hbs:39 +msgid "Sending scheduled" +msgstr "" + +#: views/campaigns/view.hbs:40 views/campaigns/view.hbs:52 +msgid "Pause" +msgstr "" + +#: views/campaigns/view.hbs:41 routes/campaigns.js:264 +msgid "Sending" +msgstr "" + +#: views/campaigns/view.hbs:42 views/campaigns/view.hbs:46 +msgid "" +"Are you sure? This action would resume sending messages to the selected list" +msgstr "" + +#: views/campaigns/view.hbs:43 views/campaigns/view.hbs:47 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" + +#: views/campaigns/view.hbs:44 +msgid "Resume" +msgstr "" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Reset" +msgstr "" + +#: views/campaigns/view.hbs:48 +msgid "Continue" +msgstr "" + +#: views/campaigns/view.hbs:50 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" + +#: views/campaigns/view.hbs:51 +msgid "" +"Are you sure? This action would pause sending new entries in RSS feed as " +"email messages to the selected list" +msgstr "" + +#: views/campaigns/view.hbs:53 views/campaigns/view.hbs:57 +msgid "Campaign status:" +msgstr "" + +#: views/campaigns/view.hbs:54 +msgid "ACTIVE" +msgstr "" + +#: views/campaigns/view.hbs:55 +msgid "" +"Are you sure? This action would start sending new entries in RSS feed as " +"email messages to the selected list" +msgstr "" + +#: views/campaigns/view.hbs:56 +msgid "Activate" +msgstr "" + +#: views/campaigns/view.hbs:58 +msgid "INACTIVE" +msgstr "" + +#: views/campaigns/view.hbs:59 +msgid "" +"This is a triggered campaign. Messages are only sent to subscribers that hit " +"some trigger that invokes this campaign" +msgstr "" + +#: views/campaigns/view.hbs:60 +msgid "see more" +msgstr "" + +#: views/campaigns/view.hbs:65 +msgid "List subscribers who clicked this link" +msgstr "" + +#: views/campaigns/view.hbs:69 +msgid "" +"Clicks are counted as unique subscribers that clicked on a specific link or " +"on any link (in aggregated view)" +msgstr "" + +#: views/campaigns/view.hbs:70 +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 "" + +#: views/emails/confirm-html.hbs:1 views/emails/confirm-html.hbs:2 +#: views/emails/confirm-text.hbs:1 +msgid "Please Confirm Subscription" +msgstr "Palun kinnita oma liitumissoov" + +#: views/emails/confirm-html.hbs:3 views/emails/confirm-text.hbs:2 +msgid "Yes, subscribe me to this list" +msgstr "Jah, soovin liituda selle listiga" + +#: views/emails/confirm-html.hbs:4 +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 "" +"Kui said selle kirja kogemata, siis lihtsalt kustuta see. Sind ei lisata " +"listi, kui sa ei kliki allolevale kinnituslingile" + +#: views/emails/confirm-html.hbs:5 views/emails/confirm-text.hbs:4 +#: views/emails/subscription-confirmed-html.hbs:7 +#: views/emails/subscription-confirmed-text.hbs:7 +#: views/emails/unsubscribe-confirmed-html.hbs:5 +#: views/emails/unsubscribe-confirmed-text.hbs:5 +msgid "For questions about this list, please contact:" +msgstr "Küsimustega seoses selle listiga võta ühendust järgmisel aadressil:" + +#: views/emails/confirm-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 "" + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-html.hbs:2 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:2 +msgid "We have received a password change request for your Mailtrain account:" +msgstr "" + +#: views/emails/password-reset-html.hbs:4 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "" + +#: views/emails/password-reset-html.hbs:5 +#: 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 "" + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "" + +#: views/emails/rss-html.hbs:2 views/emails/stationery-html.hbs:4 +#: views/emails/stationery-text.hbs:4 views/lists/subscription/edit.hbs:15 +#: views/subscription/manage.hbs:12 views/subscription/unsubscribe.hbs:1 +#: views/subscription/unsubscribe.hbs:4 routes/lists.js:253 +msgid "Unsubscribe" +msgstr "" + +#: 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 "" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "" + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "" + +#: views/emails/subscription-confirmed-html.hbs:1 +#: views/emails/subscription-confirmed-text.hbs:1 +#: views/subscription/subscribed.hbs:1 +msgid "Subscription Confirmed" +msgstr "" + +#: views/emails/subscription-confirmed-html.hbs:2 +#: views/emails/subscription-confirmed-text.hbs:2 +#: views/subscription/subscribed.hbs:2 +msgid "Your subscription to our list has been confirmed." +msgstr "" + +#: views/emails/subscription-confirmed-html.hbs:3 +#: views/emails/subscription-confirmed-text.hbs:3 +msgid "If you want to modify your subscription then you can:" +msgstr "" + +#: views/emails/subscription-confirmed-html.hbs:4 +#: views/emails/subscription-confirmed-text.hbs:4 +#: views/subscription/subscribed.hbs:6 +msgid "manage your preferences" +msgstr "" + +#: views/emails/subscription-confirmed-html.hbs:5 +#: views/emails/subscription-confirmed-text.hbs:5 +#: views/subscription/subscribed.hbs:5 views/users/login.hbs:10 +msgid "or" +msgstr "" + +#: views/emails/subscription-confirmed-html.hbs:6 +#: views/emails/subscription-confirmed-text.hbs:6 +msgid "unsubscribe here" +msgstr "" + +#: views/emails/unsubscribe-confirmed-html.hbs:1 +#: views/emails/unsubscribe-confirmed-text.hbs:1 +msgid "You are now unsubscribed" +msgstr "" + +#: views/emails/unsubscribe-confirmed-html.hbs:2 +#: views/emails/unsubscribe-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "" + +#: views/emails/unsubscribe-confirmed-html.hbs:3 +#: views/emails/unsubscribe-confirmed-text.hbs:3 +msgid "If you unsubscribed by mistake, you can re-subscribe at:" +msgstr "" + +#: views/emails/unsubscribe-confirmed-html.hbs:4 +#: views/emails/unsubscribe-confirmed-text.hbs:4 +#: views/lists/subscription/add.hbs:16 routes/lists.js:253 +msgid "Subscribe" +msgstr "" + +#: views/index.hbs:1 +msgid "Official Mailtrain Partners" +msgstr "Ametlikud Mailtraini partnerid" + +#: views/index.hbs:2 +msgid "Free, open source mail server solution" +msgstr "" + +#: views/index.hbs:3 +msgid "" +"A reliable SMTP server, easy integration, and 12,000 messages a month free" +msgstr "" + +#: views/index.hbs:4 +msgid "List management" +msgstr "" + +#: views/index.hbs:5 +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 "" + +#: views/index.hbs:6 +msgid "Custom fields" +msgstr "" + +#: views/index.hbs:7 +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 "" + +#: views/index.hbs:8 +msgid "List segmentation" +msgstr "" + +#: 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 "" + +#: views/index.hbs:10 +msgid "Donate to author" +msgstr "" + +#: views/index.hbs:11 +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 15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM" +msgstr "" + +#: views/index.hbs:12 +msgid "or donate using PayPal" +msgstr "" + +#: views/index.hbs:13 +msgid "RSS Campaigns" +msgstr "" + +#: views/index.hbs:14 +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 "" + +#: views/index.hbs:15 +msgid "GPG Encryption" +msgstr "" + +#: views/index.hbs:16 +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 "" + +#: views/index.hbs:17 +msgid "Click stats" +msgstr "" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" + +#: views/index.hbs:19 +msgid "Open source" +msgstr "" + +#: views/index.hbs:20 +msgid "Mailtrain is available under GPLv3 license and completely open source." +msgstr "" + +#: views/index.hbs:21 +msgid "Send via any provider" +msgstr "" + +#: views/index.hbs:22 +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 "" + +#: views/index.hbs:23 +msgid "Trigger based automation" +msgstr "" + +#: views/index.hbs:24 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "" + +#: views/layout.hbs:3 +msgid "Wiki" +msgstr "" + +#: views/layout.hbs:4 +msgid "Blog" +msgstr "" + +#: views/layout.hbs:5 views/users/account.hbs:2 views/users/account.hbs:3 +msgid "Account" +msgstr "" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "" + +#: views/layout.hbs:7 views/users/api.hbs:2 views/users/api.hbs:3 +msgid "API" +msgstr "" + +#: views/layout.hbs:8 +msgid "Log out" +msgstr "" + +#: views/layout.hbs:9 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 "" + +#: views/layout.hbs:10 +msgid "Self hosted newsletter app built on top of Nodemailer" +msgstr "" + +#: views/layout.hbs:11 views/layout.hbs:13 +msgid "Source on GitHub" +msgstr "" + +#: views/layout.hbs:12 +msgid "Subscribe to our newsletter" +msgstr "" + +#: 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/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:111 +msgid "Lists" +msgstr "" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:9 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "" + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:7 +msgid "Edit List" +msgstr "" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "" + +#: views/lists/edit.hbs:12 +msgid "Delete List" +msgstr "" + +#: 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:5 +msgid "Custom Fields" +msgstr "" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "" + +#: 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 "" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "" + +#: views/lists/fields/create.hbs:14 views/lists/fields/edit.hbs:15 +msgid "JSON" +msgstr "" + +#: views/lists/fields/create.hbs:15 views/lists/fields/edit.hbs:16 +msgid "Date" +msgstr "" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "" + +#: views/lists/fields/create.hbs:17 views/lists/fields/edit.hbs:18 +#: lib/models/fields.js:26 +msgid "Date (DD/MM/YYYY)" +msgstr "" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "" + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "" + +#: views/lists/fields/create.hbs:24 views/lists/fields/edit.hbs:25 +#: lib/models/fields.js:23 +msgid "Checkboxes" +msgstr "" + +#: views/lists/fields/create.hbs:25 views/lists/fields/edit.hbs:26 +msgid "Option for a group value" +msgstr "" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/lists/fields/create.hbs:33 views/lists/fields/edit.hbs:37 +msgid "Visible" +msgstr "" + +#: views/lists/fields/create.hbs:34 +msgid "Add Field" +msgstr "" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "" + +#: views/lists/fields/edit.hbs:30 views/lists/fields/fields.hbs:8 +#: views/partials/merge-tag-reference.hbs:3 +msgid "Merge tag" +msgstr "" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "" + +#: views/lists/fields/fields.hbs:7 views/lists/view.hbs:25 +msgid "Type" +msgstr "" + +#: views/lists/fields/fields.hbs:10 views/lists/fields/fields.hbs:11 +#: views/lists/lists.hbs:9 views/lists/segments/segments.hbs:8 +#: views/lists/segments/view.hbs:12 views/templates/templates.hbs:7 +#: views/triggers/triggers.hbs:14 routes/campaigns.js:287 +#: routes/campaigns.js:576 routes/campaigns.js:626 routes/lists.js:222 +#: routes/triggers.js:297 +msgid "Edit" +msgstr "" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "" + +#: views/lists/lists.hbs:7 +msgid "Subscribers" +msgstr "" + +#: 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:6 +#: views/lists/view.hbs:13 +msgid "Segments" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "" + +#: views/lists/segments/edit.hbs:4 views/lists/segments/edit.hbs:5 +#: views/lists/segments/view.hbs:6 views/lists/view.hbs:11 +msgid "Edit Segment" +msgstr "" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-edit.hbs:20 +msgid "Use relative range match" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: views/lists/segments/rule-configure.hbs:33 +msgid "Add Rule" +msgstr "" + +#: views/lists/segments/rule-create.hbs:8 +#: views/lists/subscription/import.hbs:12 views/triggers/create-select.hbs:9 +msgid "Next" +msgstr "" + +#: views/lists/segments/rule-edit.hbs:37 +msgid "Delete Rule" +msgstr "" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "" + +#: views/lists/segments/view.hbs:5 views/lists/view.hbs:12 +msgid "Segment" +msgstr "" + +#: views/lists/segments/view.hbs:7 +msgid "Match rules" +msgstr "" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "" + +#: views/lists/subscription/add.hbs:5 views/subscription/manage.hbs:2 +#: views/subscription/subscribe.hbs:3 views/users/account.hbs:7 +msgid "Email Address" +msgstr "" + +#: views/lists/subscription/add.hbs:8 views/lists/subscription/edit.hbs:9 +#: views/settings.hbs:82 views/settings.hbs:97 views/subscription/manage.hbs:7 +#: views/subscription/subscribe.hbs:7 +msgid "Begins with" +msgstr "" + +#: 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 "" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "" + +#: views/lists/subscription/edit.hbs:6 +#: views/lists/subscription/import-preview.hbs:6 +#: views/subscription/unsubscribe.hbs:3 lib/helpers.js:26 +#: lib/models/segments.js:11 +msgid "Email address" +msgstr "" + +#: views/lists/subscription/edit.hbs:16 +msgid "Delete Subscription" +msgstr "" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "" + +#: views/lists/subscription/import-failed.hbs:6 +msgid "see here" +msgstr "" + +#: views/lists/subscription/import-failed.hbs:10 +msgid "Fail reason" +msgstr "" + +#: 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 "" + +#: 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:55 +msgid "Example" +msgstr "" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "" + +#: views/lists/subscription/import.hbs:6 +msgid "CSV delimiter" +msgstr "" + +#: views/lists/subscription/import.hbs:7 +msgid "Categorize the imported subscribers as" +msgstr "" + +#: views/lists/subscription/import.hbs:8 routes/lists.js:171 +msgid "Subscribed" +msgstr "" + +#: views/lists/subscription/import.hbs:9 +msgid "Regular subscriber addresses" +msgstr "" + +#: views/lists/subscription/import.hbs:11 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "" + +#: views/lists/view.hbs:8 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 "" + +#: views/lists/view.hbs:9 +msgid "Add Subscriber" +msgstr "" + +#: views/lists/view.hbs:10 +msgid "Import Subscribers" +msgstr "" + +#: views/lists/view.hbs:14 +msgid "Create New Segment" +msgstr "" + +#: views/lists/view.hbs:15 +msgid "Filter" +msgstr "" + +#: views/lists/view.hbs:16 +msgid "Subscriptions" +msgstr "" + +#: views/lists/view.hbs:17 +msgid "Imports" +msgstr "" + +#: views/lists/view.hbs:24 routes/campaigns.js:266 routes/lists.js:265 +msgid "Finished" +msgstr "" + +#: views/lists/view.hbs:26 +msgid "Added" +msgstr "" + +#: views/lists/view.hbs:27 +msgid "Updated" +msgstr "" + +#: views/lists/view.hbs:28 +msgid "Failed" +msgstr "" + +#: views/lists/view.hbs:30 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" + +#: views/lists/view.hbs:31 +msgid "Restart" +msgstr "" + +#: views/partials/codeeditor.hbs:1 views/partials/summernote.hbs:1 +msgid "Template content (HTML)" +msgstr "" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "" + +#: views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "" + +#: 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 "" + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "" + +#: views/settings.hbs:17 +msgid "Campaign defaults" +msgstr "" + +#: views/settings.hbs:18 +msgid "Sender name" +msgstr "" + +#: views/settings.hbs:19 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "" + +#: views/settings.hbs:20 +msgid "Default address" +msgstr "" + +#: views/settings.hbs:21 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "" + +#: views/settings.hbs:22 +msgid "Default \"from name\"" +msgstr "" + +#: views/settings.hbs:24 +msgid "Default \"from\" email" +msgstr "" + +#: views/settings.hbs:26 +msgid "Default \"subject line\"" +msgstr "" + +#: views/settings.hbs:28 +msgid "Default homepage (URL)" +msgstr "" + +#: views/settings.hbs:29 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "" + +#: views/settings.hbs:30 +msgid "Mailer Settings" +msgstr "" + +#: views/settings.hbs:31 +msgid "These settings are required to send out e-mail messages" +msgstr "" + +#: views/settings.hbs:32 +msgid "SMTP" +msgstr "" + +#: views/settings.hbs:33 +msgid "AWS SES" +msgstr "" + +#: views/settings.hbs:34 +msgid "Use SMTP for sending mail" +msgstr "" + +#: views/settings.hbs:35 +msgid "Hostname" +msgstr "" + +#: views/settings.hbs:36 +msgid "Port" +msgstr "" + +#: views/settings.hbs:37 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "" + +#: views/settings.hbs:38 +msgid "Encryption" +msgstr "" + +#: views/settings.hbs:39 +msgid "Disable SMTP authentication" +msgstr "" + +#: views/settings.hbs:40 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "" + +#: views/settings.hbs:41 +msgid "Username, eg. myaccount@example.com" +msgstr "" + +#: views/settings.hbs:42 views/settings.hbs:43 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "" + +#: views/settings.hbs:44 +msgid "Use SES API for sending mail" +msgstr "" + +#: views/settings.hbs:45 +msgid "Access Key" +msgstr "" + +#: views/settings.hbs:46 +msgid "AWS Access Key Id" +msgstr "" + +#: views/settings.hbs:47 +msgid "Secret Key" +msgstr "" + +#: views/settings.hbs:48 +msgid "AWS Secret Access Key" +msgstr "" + +#: views/settings.hbs:49 +msgid "Region" +msgstr "" + +#: views/settings.hbs:50 +msgid "Checking" +msgstr "" + +#: views/settings.hbs:51 +msgid "Check Mailer config" +msgstr "" + +#: views/settings.hbs:52 +msgid "Don't have an SMTP account yet? Create a free SendPulse account" +msgstr "" + +#: views/settings.hbs:53 +msgid "here" +msgstr "" + +#: views/settings.hbs:54 +msgid "Advanced Mailer settings" +msgstr "" + +#: views/settings.hbs:55 +msgid "Log SMTP transactions" +msgstr "" + +#: views/settings.hbs:56 +msgid "Allow self-signed certificates" +msgstr "" + +#: views/settings.hbs:57 +msgid "Max connections" +msgstr "" + +#: views/settings.hbs:58 +msgid "The count of max connections, eg. 10" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:60 +msgid "Max messages" +msgstr "" + +#: views/settings.hbs:61 +msgid "The count of max messages, eg. 100" +msgstr "" + +#: views/settings.hbs:62 +msgid "" +"he number of messages to send through a single connection before the " +"connection is closed and reopened (defaults to 100)" +msgstr "" + +#: views/settings.hbs:63 +msgid "Throttling" +msgstr "" + +#: views/settings.hbs:64 +msgid "Messages per hour eg. 1000" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:66 +msgid "VERP bounce handling" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: views/settings.hbs:70 +msgid "Use VERP to catch bounces" +msgstr "" + +#: views/settings.hbs:71 +msgid "Server hostname" +msgstr "" + +#: views/settings.hbs:72 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:74 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" + +#: views/settings.hbs:75 +msgid "GPG Signing" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:77 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" + +#: views/settings.hbs:78 +msgid "Private Key Passphrase" +msgstr "" + +#: views/settings.hbs:79 +msgid "Passphrase for the key if set" +msgstr "" + +#: views/settings.hbs:80 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "" + +#: views/settings.hbs:81 +msgid "GPG Private Key" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:84 +msgid "DKIM Signing by ZoneMTA" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:86 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" + +#: views/settings.hbs:87 +msgid "ZoneMTA DKIM API Key" +msgstr "" + +#: views/settings.hbs:88 +msgid "Some secret value" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:90 +msgid "DKIM domain" +msgstr "" + +#: views/settings.hbs:91 +msgid "Domain name for the DKIM key" +msgstr "" + +#: views/settings.hbs:92 +msgid "Leave blank to use the sender email address domain" +msgstr "" + +#: views/settings.hbs:93 views/settings.hbs:94 +msgid "DKIM key selector" +msgstr "" + +#: views/settings.hbs:95 +msgid "Signing is disabled without a valid selector value" +msgstr "" + +#: views/settings.hbs:96 +msgid "DKIM Private Key" +msgstr "" + +#: views/settings.hbs:98 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" + +#: views/subscription/confirm-notice.hbs:1 views/subscription/subscribe.hbs:1 +msgid "Warning!" +msgstr "" + +#: views/subscription/confirm-notice.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "" + +#: views/subscription/confirm-notice.hbs:3 +msgid "Almost finished." +msgstr "" + +#: views/subscription/confirm-notice.hbs:4 +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 "" + +#: views/subscription/confirm-notice.hbs:5 +#: views/subscription/unsubscribe-notice.hbs:3 +#: views/subscription/updated-notice.hbs:3 +msgid "return to our website" +msgstr "" + +#: views/subscription/manage-address.hbs:1 +msgid "Update your Email Address" +msgstr "" + +#: views/subscription/manage-address.hbs:2 +msgid "Existing Email Address" +msgstr "" + +#: views/subscription/manage-address.hbs:3 +msgid "New Email Address" +msgstr "" + +#: views/subscription/manage-address.hbs:4 +msgid "Your new email address" +msgstr "" + +#: views/subscription/manage-address.hbs:5 +msgid "" +"You will receive a confirmation request to your new email address that you " +"need to accept before your email is actually changed" +msgstr "" + +#: views/subscription/manage-address.hbs:6 +msgid "Update Email Address" +msgstr "" + +#: views/subscription/manage.hbs:1 +msgid "Update your preferences" +msgstr "" + +#: views/subscription/manage.hbs:3 +msgid "want to change it?" +msgstr "" + +#: views/subscription/manage.hbs:6 views/subscription/subscribe.hbs:6 +msgid "Download signature verification key" +msgstr "" + +#: views/subscription/manage.hbs:8 views/subscription/subscribe.hbs:8 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "" + +#: views/subscription/manage.hbs:9 views/subscription/subscribe.hbs:9 +msgid "optional" +msgstr "" + +#: views/subscription/manage.hbs:11 +msgid "Update Profile" +msgstr "" + +#: views/subscription/subscribe.hbs:2 +msgid "JavaScript must be enabled in order for the subscription form to work" +msgstr "" + +#: views/subscription/subscribe.hbs:11 +msgid "Subscribe to list" +msgstr "" + +#: views/subscription/subscribed.hbs:3 +msgid "Thank you for subscribing!" +msgstr "" + +#: views/subscription/subscribed.hbs:4 +msgid "continue to our website" +msgstr "" + +#: views/subscription/unsubscribe-notice.hbs:1 +msgid "Unsubscribe Successful" +msgstr "" + +#: views/subscription/unsubscribe-notice.hbs:2 +msgid "You have been removed from:" +msgstr "" + +#: views/subscription/unsubscribe.hbs:2 +msgid "Enter your email address to unsubscribe from:" +msgstr "" + +#: views/subscription/updated-notice.hbs:1 +msgid "Profile Updated" +msgstr "" + +#: views/subscription/updated-notice.hbs:2 +msgid "Your profile information has been updated." +msgstr "" + +#: views/templates/create.hbs:2 views/templates/edit.hbs:2 +#: views/templates/templates.hbs:2 views/templates/templates.hbs:4 +#: lib/tools.js:115 +msgid "Templates" +msgstr "" + +#: views/templates/create.hbs:3 views/templates/create.hbs:4 +#: views/templates/create.hbs:12 views/templates/templates.hbs:3 +msgid "Create Template" +msgstr "" + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "" + +#: views/templates/create.hbs:7 +msgid "HTML Editor" +msgstr "" + +#: views/templates/create.hbs:10 views/templates/edit.hbs:9 +msgid "Optional comments about this template" +msgstr "" + +#: views/templates/edit.hbs:3 views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "" + +#: views/templates/edit.hbs:11 +msgid "Delete Template" +msgstr "" + +#: 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 "" + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "" + +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "" + +#: views/triggers/create.hbs:16 views/triggers/create.hbs:21 +#: views/triggers/edit.hbs:18 views/triggers/edit.hbs:23 +msgid "Event" +msgstr "" + +#: 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 "" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "" + +#: views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "" + +#: views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "" + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you lose your password" +msgstr "" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "" + +#: views/users/account.hbs:16 +msgid "Confirm Password" +msgstr "" + +#: views/users/account.hbs:17 views/users/reset.hbs:8 +msgid "Confirm New Password" +msgstr "" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "" + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/users/api.hbs:13 +msgid "Add subscription" +msgstr "" + +#: 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 "" + +#: 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:48 +msgid "arguments" +msgstr "" + +#: views/users/api.hbs:16 views/users/api.hbs:31 views/users/api.hbs:39 +#: views/users/api.hbs:47 +msgid "your personal access token" +msgstr "" + +#: views/users/api.hbs:18 views/users/api.hbs:33 views/users/api.hbs:41 +msgid "subscriber's email address" +msgstr "" + +#: views/users/api.hbs:19 views/users/api.hbs:34 views/users/api.hbs:42 +#: views/users/api.hbs:50 +msgid "required" +msgstr "" + +#: views/users/api.hbs:20 +msgid "subscriber's first name" +msgstr "" + +#: views/users/api.hbs:21 +msgid "subscriber's last name" +msgstr "" + +#: views/users/api.hbs:22 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" + +#: views/users/api.hbs:23 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" + +#: views/users/api.hbs:24 +msgid "Additional POST arguments" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/users/api.hbs:28 +msgid "Remove subscription" +msgstr "" + +#: views/users/api.hbs:29 +msgid "This API call marks a subscription as unsubscribed" +msgstr "" + +#: views/users/api.hbs:36 +msgid "Delete subscription" +msgstr "" + +#: views/users/api.hbs:37 +msgid "This API call deletes a subscription" +msgstr "" + +#: views/users/api.hbs:44 +msgid "Add new custom field" +msgstr "" + +#: views/users/api.hbs:45 +msgid "This API call creates a new custom field for a list." +msgstr "" + +#: views/users/api.hbs:49 +msgid "field name" +msgstr "" + +#: views/users/api.hbs:51 +msgid "one of the following types:" +msgstr "" + +#: views/users/api.hbs:52 +msgid "" +"If the type is 'option' then you also need to specify the parent element ID" +msgstr "" + +#: views/users/api.hbs:53 +msgid "" +"Template for the group element. If not set, then values of the elements are " +"joined with commas" +msgstr "" + +#: views/users/api.hbs:54 +msgid "" +"if not visible then the subscriber can not view or modify this value at the " +"profile page" +msgstr "" + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "" + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "" + +#: 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 "" + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "" + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "" + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "" + +#: lib/helpers.js:17 +msgid "URL that points to the unsubscribe page" +msgstr "" + +#: lib/helpers.js:20 +msgid "URL that points to the preferences page of the subscriber" +msgstr "" + +#: lib/helpers.js:23 +msgid "URL to preview the message in a browser" +msgstr "" + +#: lib/helpers.js:29 lib/models/segments.js:31 +msgid "First name" +msgstr "" + +#: lib/helpers.js:32 lib/models/segments.js:35 +msgid "Last name" +msgstr "" + +#: lib/helpers.js:35 +msgid "Full name (first and last name combined)" +msgstr "" + +#: lib/helpers.js:38 +msgid "Unique ID that identifies the recipient" +msgstr "" + +#: lib/helpers.js:41 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "" + +#: lib/helpers.js:44 +msgid "Unique ID that identifies current campaign" +msgstr "" + +#: lib/mailer.js:215 +msgid "Invalid mail transport" +msgstr "Vigane maili transport" + +#: lib/models/campaigns.js:271 lib/models/campaigns.js:298 +#: lib/models/campaigns.js:371 lib/models/campaigns.js:494 +#: lib/models/campaigns.js:752 lib/models/campaigns.js:881 +msgid "Missing Campaign ID" +msgstr "" + +#: lib/models/campaigns.js:407 +msgid "Emtpy or too large attahcment" +msgstr "" + +#: lib/models/campaigns.js:573 lib/models/campaigns.js:761 +msgid "Campaign Name must be set" +msgstr "" + +#: lib/models/campaigns.js:577 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "" + +#: lib/models/campaigns.js:730 +msgid "Selected template not found" +msgstr "" + +#: lib/models/campaigns.js:1082 +msgid "Invalid or missing message ID" +msgstr "" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/lists.js:81 lib/models/lists.js:175 lib/models/lists.js:212 +#: lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:88 lib/models/subscriptions.js:640 +#: lib/models/subscriptions.js:703 lib/models/subscriptions.js:889 +#: lib/models/subscriptions.js:992 lib/models/subscriptions.js:1046 +#: lib/models/subscriptions.js:1109 lib/models/subscriptions.js:1152 +msgid "Missing List ID" +msgstr "" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "" + +#: lib/models/links.js:328 routes/campaigns.js:541 routes/campaigns.js:590 +#: services/sender.js:304 +msgid "Campaign not found" +msgstr "" + +#: lib/models/links.js:336 routes/lists.js:146 services/sender.js:311 +msgid "List not found" +msgstr "" + +#: lib/models/links.js:344 +msgid "Subscription not found" +msgstr "" + +#: lib/models/lists.js:117 lib/models/lists.js:179 +msgid "List Name must be set" +msgstr "" + +#: lib/models/lists.js:241 +msgid "Missing List CID" +msgstr "" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "" + +#: lib/models/segments.js:19 lib/models/triggers.js:11 +msgid "Sign up date" +msgstr "" + +#: lib/models/segments.js:23 lib/models/triggers.js:15 +msgid "Latest open" +msgstr "" + +#: lib/models/segments.js:27 lib/models/triggers.js:19 +msgid "Latest click" +msgstr "" + +#: 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 "" + +#: lib/models/segments.js:85 lib/models/segments.js:549 +#: lib/models/segments.js:658 +msgid "Segment not found" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: lib/models/segments.js:148 lib/models/segments.js:410 +msgid "today" +msgstr "" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: lib/models/segments.js:358 lib/models/segments.js:434 +#: lib/models/segments.js:524 +msgid "Missing Rule ID" +msgstr "" + +#: lib/models/segments.js:374 +msgid "Specified rule not found" +msgstr "" + +#: lib/models/segments.js:385 +msgid "Specified segment not found" +msgstr "" + +#: lib/models/segments.js:445 +msgid "Selected rule not found" +msgstr "" + +#: lib/models/subscriptions.js:233 +msgid "%s: Please Confirm Subscription" +msgstr "" + +#: lib/models/subscriptions.js:324 +msgid "Could not save subscription" +msgstr "" + +#: lib/models/subscriptions.js:507 lib/models/subscriptions.js:537 +msgid "Missing Subscription ID" +msgstr "" + +#: lib/models/subscriptions.js:565 +msgid "Missing Subscription email address" +msgstr "" + +#: lib/models/subscriptions.js:644 lib/models/subscriptions.js:893 +#: lib/models/subscriptions.js:1156 +msgid "Missing subscription ID" +msgstr "" + +#: lib/models/subscriptions.js:707 +msgid "Missing email address" +msgstr "" + +#: lib/models/subscriptions.js:996 lib/models/subscriptions.js:1050 +#: lib/models/subscriptions.js:1086 +msgid "Missing Import ID" +msgstr "" + +#: lib/models/subscriptions.js:1178 +msgid "Unknown subscription ID" +msgstr "" + +#: lib/models/subscriptions.js:1183 +msgid "Nothing seems to be changed" +msgstr "" + +#: lib/models/subscriptions.js:1197 +msgid "This address is already registered by someone else" +msgstr "" + +#: lib/models/templates.js:51 lib/models/templates.js:122 +#: lib/models/templates.js:163 +msgid "Missing Template ID" +msgstr "" + +#: lib/models/templates.js:80 lib/models/templates.js:126 +msgid "Template Name must be set" +msgstr "" + +#: lib/models/triggers.js:28 +msgid "Has Opened" +msgstr "" + +#: lib/models/triggers.js:31 +msgid "Has Clicked" +msgstr "" + +#: lib/models/triggers.js:34 +msgid "Not Opened" +msgstr "" + +#: lib/models/triggers.js:37 +msgid "Not Clicked" +msgstr "" + +#: lib/models/triggers.js:174 lib/models/triggers.js:211 +msgid "Missing or invalid list ID" +msgstr "" + +#: lib/models/triggers.js:178 lib/models/triggers.js:263 +msgid "Days in the past are not allowed" +msgstr "" + +#: lib/models/triggers.js:182 lib/models/triggers.js:203 +#: lib/models/triggers.js:267 lib/models/triggers.js:288 +msgid "Missing or invalid trigger rule" +msgstr "" + +#: lib/models/triggers.js:189 lib/models/triggers.js:274 +msgid "Invalid subscription configuration" +msgstr "" + +#: lib/models/triggers.js:196 lib/models/triggers.js:281 +msgid "Invalid campaign configuration" +msgstr "" + +#: lib/models/triggers.js:199 lib/models/triggers.js:284 +msgid "A campaing can not be a target for itself" +msgstr "" + +#: lib/models/triggers.js:232 +msgid "Could not store trigger row" +msgstr "" + +#: lib/models/triggers.js:249 +msgid "Missing or invalid Trigger ID" +msgstr "" + +#: lib/models/triggers.js:316 +msgid "Missing Trigger ID" +msgstr "" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "" + +#: lib/passport.js:38 +msgid "%s logged out" +msgstr "" + +#: lib/passport.js:51 +msgid "Failed to authenticate user" +msgstr "" + +#: lib/passport.js:67 +msgid "Logged in as %s" +msgstr "" + +#: lib/passport.js:125 +msgid "Incorrect username or password" +msgstr "" + +#: lib/tools.js:123 +msgid "Automation" +msgstr "" + +#: lib/tools.js:133 +msgid "Blocked email address \"%s\"" +msgstr "" + +#: lib/tools.js:142 +msgid "Invalid email address \"%s\"." +msgstr "" + +#: lib/tools.js:145 +msgid "MX record not found for domain" +msgstr "" + +#: lib/tools.js:148 +msgid "Address domain not found" +msgstr "" + +#: lib/tools.js:151 +msgid "Address domain name is required" +msgstr "" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:211 +msgid "Not Found" +msgstr "Lehekülge ei leitud :(" + +#: routes/archive.js:110 services/sender.js:447 +msgid "Received status code %s from %s" +msgstr "" + +#: routes/archive.js:134 routes/campaigns.js:131 routes/campaigns.js:295 +#: routes/campaigns.js:390 routes/campaigns.js:435 routes/campaigns.js:475 +#: routes/campaigns.js:739 routes/campaigns.js:762 routes/campaigns.js:781 +#: routes/campaigns.js:803 routes/triggers.js:146 +msgid "Could not find campaign with specified ID" +msgstr "" + +#: routes/archive.js:142 routes/campaigns.js:789 +msgid "Attachment not found" +msgstr "" + +#: routes/campaigns.js:26 routes/fields.js:13 routes/lists.js:49 +#: routes/segments.js:13 routes/settings.js:23 routes/templates.js:17 +#: routes/triggers.js:18 routes/users.js:75 routes/users.js:120 +msgid "Need to be logged in to access restricted content" +msgstr "Pead olema sisse logitud, et näha peidetud sisu" + +#: routes/campaigns.js:117 +msgid "Could not create campaign" +msgstr "" + +#: routes/campaigns.js:120 +msgid "Campaign “%s” created" +msgstr "" + +#: routes/campaigns.js:204 +msgid "content from an RSS entry" +msgstr "" + +#: routes/campaigns.js:220 +msgid "Campaign settings updated" +msgstr "" + +#: routes/campaigns.js:222 +msgid "Campaign settings not updated" +msgstr "" + +#: routes/campaigns.js:238 routes/campaigns.js:639 +msgid "Campaign deleted" +msgstr "" + +#: routes/campaigns.js:240 routes/campaigns.js:641 +msgid "Could not delete specified campaign" +msgstr "" + +#: routes/campaigns.js:259 +msgid "Idling" +msgstr "" + +#: routes/campaigns.js:262 +msgid "Scheduled" +msgstr "" + +#: routes/campaigns.js:268 +msgid "Paused" +msgstr "" + +#: routes/campaigns.js:270 +msgid "Inactive" +msgstr "" + +#: routes/campaigns.js:272 +msgid "Active" +msgstr "" + +#: routes/campaigns.js:274 +msgid "Other" +msgstr "" + +#: routes/campaigns.js:429 +msgid "Unknown status selector" +msgstr "" + +#: routes/campaigns.js:657 +msgid "Scheduled sending" +msgstr "" + +#: routes/campaigns.js:659 +msgid "Could not schedule sending" +msgstr "" + +#: routes/campaigns.js:671 +msgid "Sending resumed" +msgstr "" + +#: routes/campaigns.js:673 +msgid "Could not resume sending" +msgstr "" + +#: routes/campaigns.js:685 +msgid "Sending reset" +msgstr "" + +#: routes/campaigns.js:687 +msgid "Could not reset sending" +msgstr "" + +#: routes/campaigns.js:699 routes/campaigns.js:727 +msgid "Sending paused" +msgstr "" + +#: routes/campaigns.js:701 routes/campaigns.js:729 +msgid "Could not pause sending" +msgstr "" + +#: routes/campaigns.js:713 +msgid "Sending activated" +msgstr "" + +#: routes/campaigns.js:715 +msgid "Could not activate sending" +msgstr "" + +#: routes/campaigns.js:750 +msgid "Attachment uploaded" +msgstr "" + +#: routes/campaigns.js:752 +msgid "Could not store attachment" +msgstr "" + +#: routes/campaigns.js:769 +msgid "Attachment deleted" +msgstr "" + +#: routes/campaigns.js:771 +msgid "Could not delete attachment" +msgstr "" + +#: routes/fields.js:28 routes/fields.js:64 routes/fields.js:118 +#: 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 "" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "" + +#: routes/links.js:40 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "" + +#: routes/lists.js:90 +msgid "Could not create list" +msgstr "" + +#: routes/lists.js:93 +msgid "List created" +msgstr "" + +#: routes/lists.js:101 routes/lists.js:236 routes/lists.js:301 +#: routes/lists.js:340 routes/lists.js:409 routes/lists.js:434 +#: routes/lists.js:479 routes/lists.js:501 routes/lists.js:530 +#: routes/lists.js:609 routes/lists.js:666 routes/lists.js:693 +msgid "Could not find list with specified ID" +msgstr "" + +#: routes/lists.js:115 +msgid "List settings updated" +msgstr "" + +#: routes/lists.js:117 +msgid "List settings not updated" +msgstr "" + +#: routes/lists.js:133 +msgid "List deleted" +msgstr "" + +#: routes/lists.js:135 +msgid "Could not delete specified list" +msgstr "" + +#: routes/lists.js:171 +msgid "Unknown" +msgstr "" + +#: routes/lists.js:171 +msgid "Complained" +msgstr "" + +#: routes/lists.js:202 +msgid "Invalid key" +msgstr "" + +#: routes/lists.js:204 +msgid "Expired key" +msgstr "" + +#: routes/lists.js:206 +msgid "Revoked key" +msgstr "" + +#: routes/lists.js:256 +msgid "Initializing" +msgstr "" + +#: routes/lists.js:259 +msgid "Initialized" +msgstr "" + +#: routes/lists.js:262 +msgid "Importing" +msgstr "" + +#: routes/lists.js:268 +msgid "Errored" +msgstr "" + +#: routes/lists.js:346 routes/lists.js:415 routes/lists.js:440 +msgid "Could not find subscriber with specified ID" +msgstr "" + +#: routes/lists.js:392 +msgid "Could not add subscription" +msgstr "" + +#: routes/lists.js:397 +msgid "%s was successfully added to your list" +msgstr "" + +#: routes/lists.js:399 +msgid "%s was not added to your list" +msgstr "" + +#: routes/lists.js:421 +msgid "Could not unsubscribe user" +msgstr "" + +#: routes/lists.js:424 +msgid "%s was successfully unsubscribed from your list" +msgstr "" + +#: routes/lists.js:444 +msgid "%s was successfully removed from your list" +msgstr "" + +#: routes/lists.js:456 +msgid "Another subscriber with email address %s already exists" +msgstr "" + +#: routes/lists.js:463 +msgid "Subscription settings updated" +msgstr "" + +#: routes/lists.js:465 +msgid "Subscription settings not updated" +msgstr "" + +#: routes/lists.js:507 routes/lists.js:615 routes/lists.js:651 +#: routes/lists.js:679 routes/lists.js:699 +msgid "Could not find import data with specified ID" +msgstr "" + +#: routes/lists.js:538 +msgid "Could not process CSV" +msgstr "" + +#: routes/lists.js:547 +msgid "Could not create importer" +msgstr "" + +#: routes/lists.js:598 +msgid "Empty file" +msgstr "" + +#: routes/lists.js:655 +msgid "Import started" +msgstr "" + +#: routes/lists.js:683 +msgid "Import restarted" +msgstr "" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "Kasuta TLSi" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "tavaliselt valitakse, kui port on 465" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "Kasuta STARTTLSi" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "tavaliselt valitakse, kui port on 587 või 25" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "Ära kasuta ühenduse krüpteerimist" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "Seaded uuendatud" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "Viga maili transpordi tüüp" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "Vigae ligipääsuvõti" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "VIgased AWS võtmed" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "Ühendusest keelduti, kontrolli domeeninime ja porti" + +#: 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 "" +"Ei saanud serverilt vastust, see juhtub tavaliselt kui ühendus TLS " +"serverisse ilma TLS kasutamata" + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "Ei saanud serverilt vastust" + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" +"Ühendus aegus. Kontrolli oma tulemüüri seadeid, tõenäoliselt on serveir port " +"blokeeritud" + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "Autentimist ei lubatud, server nõuab STARTTLS kasutamist" + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "Autentimine ebaõnnestus, kontrolli kasutajanime ja parooli" + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "E-posti seadistuse kontroll ebaõnnestus" + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "Server vastas \"%s\"" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "E-posti seaded on kontrollitud, võid hakata kirju saatma" + +#: routes/subscription.js:22 +msgid "Selected subscription not found" +msgstr "" + +#: routes/subscription.js:32 routes/subscription.js:103 +#: routes/subscription.js:141 routes/subscription.js:166 +#: routes/subscription.js:191 routes/subscription.js:232 +#: routes/subscription.js:270 routes/subscription.js:317 +#: routes/subscription.js:339 routes/subscription.js:368 +#: routes/subscription.js:392 routes/subscription.js:424 +msgid "Selected list not found" +msgstr "" + +#: routes/subscription.js:78 routes/subscription.js:472 +msgid "%s: Subscription Confirmed" +msgstr "" + +#: routes/subscription.js:217 +msgid "Email address not set" +msgstr "" + +#: routes/subscription.js:255 +msgid "Could not store confirmation data" +msgstr "" + +#: routes/subscription.js:284 routes/subscription.js:349 +#: routes/subscription.js:402 +msgid "Subscription not found from this list" +msgstr "" + +#: routes/subscription.js:383 +msgid "Email address updated, check your mailbox for verification instructions" +msgstr "" + +#: routes/subscription.js:499 routes/subscription.js:515 +msgid "Public key is not set" +msgstr "" + +#: routes/templates.js:98 +msgid "Could not create template" +msgstr "" + +#: routes/templates.js:101 +msgid "Template created" +msgstr "" + +#: routes/templates.js:109 +msgid "Could not find template with specified ID" +msgstr "" + +#: routes/templates.js:140 +msgid "Template settings updated" +msgstr "" + +#: routes/templates.js:142 +msgid "Template settings not updated" +msgstr "" + +#: routes/templates.js:158 +msgid "Template deleted" +msgstr "" + +#: routes/templates.js:160 +msgid "Could not delete specified template" +msgstr "" + +#: routes/triggers.js:62 routes/triggers.js:79 routes/triggers.js:154 +msgid "Could not find selected list" +msgstr "" + +#: routes/triggers.js:131 +msgid "Could not create trigger" +msgstr "" + +#: routes/triggers.js:138 +msgid "Trigger “%s” created" +msgstr "" + +#: routes/triggers.js:214 +msgid "Trigger settings updated" +msgstr "" + +#: routes/triggers.js:216 +msgid "Trigger settings not updated" +msgstr "" + +#: routes/triggers.js:228 +msgid "Trigger deleted" +msgstr "" + +#: routes/triggers.js:230 +msgid "Could not delete specified trigger" +msgstr "" + +#: routes/triggers.js:242 +msgid "Could not find trigger with specified ID" +msgstr "" + +#: routes/triggers.js:255 +msgid "Trigger not found" +msgstr "" + +#: 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 "" + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "" + +#: routes/users.js:87 +msgid "User data not found" +msgstr "" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "" + +#: services/feedcheck.js:143 +msgid "RSS entry %s" +msgstr "" + +#: services/importer.js:243 +msgid "Could not access import file" +msgstr "" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "" diff --git a/languages/fr_FR.mo b/languages/fr_FR.mo new file mode 100644 index 00000000..8ffb0982 Binary files /dev/null and b/languages/fr_FR.mo differ diff --git a/languages/fr_FR.po b/languages/fr_FR.po new file mode 100644 index 00000000..0b493f3b --- /dev/null +++ b/languages/fr_FR.po @@ -0,0 +1,4796 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Language-Team: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2018-08-11 13:55+0200\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2018-08-09 16:59+0200\n" +"X-Generator: Poedit 1.8.7.1\n" +"Last-Translator: \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Language: fr_FR\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-SearchPath-0: .\n" + +#: views/archive/layout.hbs:1 views/layout.hbs:1 +msgid "Self hosted email newsletter app" +msgstr "Solution de Newsletter auto-hébergée" + +#: 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 "Accueil" + +#: views/blacklist.hbs:2 views/blacklist.hbs:3 views/layout.hbs:7 +#: views/lists/subscription/edit.hbs:15 +msgid "Blacklist" +msgstr "Liste noire" + +#: views/blacklist.hbs:4 views/users/api.hbs:76 +msgid "Add email to blacklist" +msgstr "Ajouter à la liste noire" + +#: views/blacklist.hbs:5 +msgid "Add" +msgstr "Ajouter" + +#: views/blacklist.hbs:6 +msgid "Email" +msgstr "Email" + +#: 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 "Campagnes" + +#: views/campaigns/blacklisted.hbs:3 views/campaigns/blacklisted.hbs:4 +msgid "Blacklisted info" +msgstr "Information de liste noire" + +#: 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 "Voir la campagne" + +#: views/campaigns/blacklisted.hbs:6 +msgid "Subscribers who blacklisted by global blacklist:" +msgstr "Listes des abonnés faisant partie de la liste noire globale :" + +#: 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 "Adresse" + +#: 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 "Prénom" + +#: 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 "Nom" + +#: views/campaigns/blacklisted.hbs:10 +msgid "Reason" +msgstr "Raison" + +#: views/campaigns/blacklisted.hbs:11 +msgid "Time" +msgstr "Heure" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Informations sur les rejets" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Abonnés en erreur qui ont été désinscrits :" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "Réponse SMTP" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Heure du rejet" + +#: 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:30 +msgid "Create Campaign" +msgstr "Créer une campagne" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Campagne ordinaire" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "Campagne RSS" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "Campagne pilotée par déclencheurs" + +#: 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 "Nom" + +#: 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 "Description" + +#: 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 "Statut" + +#: 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 "Créé" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Information sur les liens" + +#: 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 "Clics" + +#: views/campaigns/clicked.hbs:8 views/campaigns/view.hbs:65 +msgid "% of clicks" +msgstr "% de clics" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:66 +msgid "% of messages" +msgstr "% de messages" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:69 +msgid "Aggregated clicks" +msgstr "Clics agrégés" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Abonnés ayant cliqué sur un lien :" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Abonnés ayant cliqué sur ce lien :" + +#: views/campaigns/clicked.hbs:13 views/campaigns/opened.hbs:7 +msgid "Stats by country" +msgstr "Statistiques par pays" + +#: views/campaigns/clicked.hbs:14 views/campaigns/opened.hbs:8 +msgid "Stats by device type" +msgstr "Statistiques par type d'équipement" + +#: views/campaigns/clicked.hbs:18 +msgid "First click time" +msgstr "Heure du premier clic" + +#: views/campaigns/clicked.hbs:19 +msgid "Click count" +msgstr "Nombre de clics" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Information de plainte" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Abonnés s'étant plaint et ayant été désinscrits :" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Heure de la plainte" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:21 +msgid "Create RSS Campaign" +msgstr "Créer une campagne 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 "" +"Une campagne RSS met en place un traqueur de flux RSS. Dès qu'une nouvelle " +"entrée est trouvée dans le flux, elle est envoyée à la liste sous forme mail." + +#: 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 "Nom de la campagne" + +#: 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 "Le HTML est autorisé" + +#: 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 "Liste" + +#: 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 "Sélectionner" + +#: 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 "abonnés" + +#: views/campaigns/create-rss.hbs:13 views/campaigns/edit-rss.hbs:15 +msgid "RSS Feed Url" +msgstr "URL du flux RSS" + +#: 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 "" +"Les nouvelles entrées de ce flux RSS seront envoyées aux abonnés sous forme " +"de messages mail" + +#: 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 "Nom de l'émetteur du message" + +#: 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:25 +msgid "This is the name your emails will come from" +msgstr "Il s'agit du nom d'émetteur utilisé pour les messages de la liste" + +#: 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 "Adresse de l'émetteur du message" + +#: 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:27 +msgid "This is the address people will send replies to" +msgstr "L'adresse à laquelle les utilisateurs enverront leurs réponses" + +#: views/campaigns/create-rss.hbs:19 views/campaigns/create-triggered.hbs:24 +#: views/campaigns/create.hbs:28 views/campaigns/edit-rss.hbs:22 +#: views/campaigns/edit-triggered.hbs:22 views/campaigns/edit.hbs:27 +msgid "Disable opened tracking" +msgstr "Désactiver le suivi des ouvertures" + +#: views/campaigns/create-rss.hbs:20 views/campaigns/create-triggered.hbs:25 +#: views/campaigns/create.hbs:29 views/campaigns/edit-rss.hbs:23 +#: views/campaigns/edit-triggered.hbs:23 views/campaigns/edit.hbs:28 +msgid "Disable clicked tracking" +msgstr "Désactiver le suivi des clics" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "Créer une campagne programmée" + +#: 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 "Modèle" + +#: views/campaigns/create-triggered.hbs:13 views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "Sélectionnez un modèle :" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "Modèle de base pour la création de la campagne" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "" +"Ou bien utiliser à la place une URL pour récupérer le contenu du message :" + +#: views/campaigns/create-triggered.hbs:17 views/campaigns/create.hbs:17 +#: views/campaigns/edit-triggered.hbs:26 views/campaigns/edit.hbs:31 +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 "" +"Lorsqu'un message est envoyé, cette URL sera POSTée avec les champs de " +"publipostage dans le corps de la requête. Utilisez cette fonctionnalité si " +"vous voulez générer le message HTML par vous-même" + +#: 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 "Objet du message" + +#: 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:29 +msgid "Keep it relevant and non-spammy" +msgstr "Adéquat et non raccoleur" + +#: 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 "" +"L'adresse à laquelle les utilisateurs enverront leurs réponses, sauf si une " +"autre adresse Reply-to a été précisée" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "Adresse de réponse Reply-to du message" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "" +"Si renseignée, les utilisateurs enverront leurs réponses à cette adresse" + +#: views/campaigns/create.hbs:26 views/campaigns/edit.hbs:25 +msgid "Custom unsubscribe (URL)" +msgstr "Désabonnement personnalisé (URL)" + +#: views/campaigns/create.hbs:27 views/campaigns/edit.hbs:26 +msgid "Set a custom unsubscribe url" +msgstr "Renseignez une URL de désabonnement personnalisée" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "Information de remise" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" +"Abonnés ayant reçu le message sans erreur, et ne s'étant pas désinscrit :" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "Heure de remise" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "Modifier la campagne 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 "Paramètres généraux" + +#: 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 "" +"Utiliser le champ de publipostage spécial [RSS_ENTRY] pour désigner " +"l'emplacement où sera inséré le contenu venant du RSS. Vous pouvez utiliser " +"par ailleurs tout autre champ disponible." + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:37 +msgid "Delete Campaign" +msgstr "Supprimer la campagne" + +#: views/campaigns/edit-rss.hbs:25 views/campaigns/edit-triggered.hbs:28 +#: views/campaigns/edit.hbs:38 views/lists/edit.hbs:22 +#: 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:41 views/lists/subscription/edit.hbs:18 +#: views/reports/edit.hbs:6 views/settings.hbs:102 views/templates/edit.hbs:13 +#: views/triggers/edit.hbs:30 views/users/account.hbs:18 +msgid "Update" +msgstr "Enregistrer" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "Modifier la campagne programmée" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +#: routes/forms.js:143 +msgid "General" +msgstr "Général" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:29 +msgid "Template Settings" +msgstr "Paramètres du modèle" + +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:30 +msgid "Template URL" +msgstr "URL du modèle" + +#: 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 "Modifier la campagne" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:32 +msgid "Attachments" +msgstr "Pièces jointes" + +#: views/campaigns/edit.hbs:33 +msgid "File" +msgstr "Fichier" + +#: views/campaigns/edit.hbs:34 +msgid "Size" +msgstr "Taille" + +#: views/campaigns/edit.hbs:35 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 "Aucune donnée dans cette table" + +#: views/campaigns/edit.hbs:36 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "Ajouter une pièce jointe" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "Informations d'ouverture" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "Abonnés ayant ouvert ce message :" + +#: views/campaigns/opened.hbs:12 +msgid "First open" +msgstr "Première ouverture" + +#: views/campaigns/opened.hbs:13 +msgid "Opened count" +msgstr "Nombre d'ouverture" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "Informations de désabonnement" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "Abonnés s'étant désinscrits" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:28 +#: views/lists/subscription/import.hbs:13 routes/lists.js:206 +msgid "Unsubscribed" +msgstr "Désabonnés" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "Télécharger" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "Aperçu" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "Liens" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "URL du flux" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "Dernière vérification" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "Pas encore vérifié" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "" +"Activer la campagne pour commencer à surveiller l'arrivée de nouveaux " +"messages dans le flux" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "Statut RSS" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "Prévisualiser la campagne comme" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "Ajouter un nouvel utilisateur de test" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "Aucun utilisateur de test, créez-en un ici" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "Lancer" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:28 +msgid "Delivered" +msgstr "Remis" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "Abonnés ayant reçu ce message" + +#: views/campaigns/view.hbs:22 +msgid "Blacklisted" +msgstr "Mis en liste noire" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who blacklisted by global blacklist" +msgstr "Listes des abonnés faisant partie de la liste noire globale" + +#: views/campaigns/view.hbs:24 routes/lists.js:206 +msgid "Bounced" +msgstr "Erreurs de distribution" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who bounced" +msgstr "Abonnés en erreur" + +#: views/campaigns/view.hbs:26 +msgid "Complaints" +msgstr "Plaintes" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who complained for this message" +msgstr "Abonnés s'étant plaint après ce message" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who unsubscribed after this message" +msgstr "Abonnés s'étant désinscrits après ce message" + +#: views/campaigns/view.hbs:30 +msgid "Opened" +msgstr "Ouvert" + +#: views/campaigns/view.hbs:31 +msgid "List subscribers who opened this message" +msgstr "Abonnés ayant ouvert ce message" + +#: views/campaigns/view.hbs:32 +msgid "Clicked" +msgstr "Cliqué" + +#: views/campaigns/view.hbs:33 views/campaigns/view.hbs:70 +msgid "List subscribers who clicked on a link" +msgstr "Abonnés ayant cliqué sur un lien" + +#: views/campaigns/view.hbs:34 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" +"Êtes-vous sûr ? Cette action va débuter l'envoi de messages à la liste " +"sélectionnée" + +#: views/campaigns/view.hbs:35 +msgid "Delay sending" +msgstr "Envoi programmé" + +#: views/campaigns/view.hbs:36 +msgid "hours" +msgstr "heures" + +#: views/campaigns/view.hbs:37 +msgid "minutes" +msgstr "minutes" + +#: views/campaigns/view.hbs:38 +msgid "Send to subscribers:" +msgstr "Envoyer aux abonnés" + +#: views/campaigns/view.hbs:39 +msgid "Are you sure? This action would reset scheduling" +msgstr "Êtes-vous sûr ? Cette action va réinitialiser les tâches programmées" + +#: views/campaigns/view.hbs:40 +msgid "Cancel" +msgstr "Annuler" + +#: views/campaigns/view.hbs:41 +msgid "Sending scheduled" +msgstr "Envoi programmé" + +#: views/campaigns/view.hbs:42 views/campaigns/view.hbs:54 +msgid "Pause" +msgstr "Pause" + +#: views/campaigns/view.hbs:43 routes/campaigns.js:254 +msgid "Sending" +msgstr "Envoi" + +#: 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 "" +"Êtes-vous sûr ? Cette action va suspendre l'envoi de messages à la liste " +"sélectionnée" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" +"Êtes-vous sûr ? Cette action va supprimer toutes les statistiques sur les " +"tâches en cours" + +#: views/campaigns/view.hbs:46 +msgid "Resume" +msgstr "Reprendre" + +#: views/campaigns/view.hbs:47 views/campaigns/view.hbs:51 +msgid "Reset" +msgstr "Réinitialiser" + +#: views/campaigns/view.hbs:50 +msgid "Continue" +msgstr "Continuer" + +#: views/campaigns/view.hbs:52 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" +"Tous les messages ont été envoyés ! Choisissez \"Continuer\" si vous voulez " +"envoyer cette campagne aux nouveaux abonnés" + +#: 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 "" +"Êtes-vous sûr ? Cette action va suspendre l'envoi de mail depuis un flux RSS " +"pour cette liste" + +#: views/campaigns/view.hbs:55 views/campaigns/view.hbs:59 +msgid "Campaign status:" +msgstr "État de la campagne :" + +#: views/campaigns/view.hbs:56 +msgid "ACTIVE" +msgstr "ACTIF" + +#: 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 "" +"Êtes-vous sûr ? Cette action va débuter l'envoi de messages depuis les " +"nouvelles entrées du flux RSS à la liste sélectionnée" + +#: views/campaigns/view.hbs:58 +msgid "Activate" +msgstr "Activer" + +#: views/campaigns/view.hbs:60 +msgid "INACTIVE" +msgstr "INACTIVE" + +#: 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 "" +"Il s'agit d'une campagne programmée. Les messages ne sont envoyés qu'aux " +"abonnés dont un événement active un déclencheur." + +#: views/campaigns/view.hbs:62 +msgid "see more" +msgstr "voir plus" + +#: views/campaigns/view.hbs:67 +msgid "List subscribers who clicked this link" +msgstr "Abonnés ayant cliqué ce lien" + +#: 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 "" +"Les clics représentent le nombre d'abonnés uniques qui ont cliqué sur un " +"lien spécifique, ou sur n'importe quel lien en vue aggrégée." + +#: 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 "" +"Lorsqu'une nouvelle entrée est trouvée dans un flux RSS, une nouvelle " +"campagne est créée à partir de cette entrée et sera listée ici" + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "Changez votre mot de passe" + +#: 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 "" +"Nous avons reçu une demande de changement de mot de passe pour votre compte " +"Mailtrain :" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "Réinitialiser le mot de passe" + +#: 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 vous n'avez pas demandé le changement de votre mot de passe, vous pouvez " +"ignorer ce message et votre mot de passe restera inchangé." + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "Préférences" + +#: 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 "Se désabonner" + +#: 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 "Consulter dans votre navigateur" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "Bonjour [FIRST_NAME/Customer]," + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "Cordialement," + +#: views/index.hbs:1 +msgid "List Management" +msgstr "Gestion de listes" + +#: 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 vous permet de gérer facilement vos listes, même très grandes. Des " +"millions d'abonnés ? Aucun problème. Vous pouvez ajouter des abonnés " +"manuellement, via l'API ou en les important depuis un fichier CSV. Toutes " +"les listes disposent de champs de publipostage personnalisables." + +#: 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 "Voir plus" + +#: 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 "Champs personnalisés" + +#: 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 "" +"Champs texte, nombres, menus déroulants ou cases à cocher, Mailtrain dispose " +"de tout. N'importe quel champ personnalisé peut être inclus dans la " +"newsletter grâce à des étiquettes de fusionnement." + +#: views/index.hbs:6 +msgid "Mailtrain also supports custom forms." +msgstr "Mailtrain permet également de personnaliser les formulaires." + +#: views/index.hbs:8 +msgid "List Segmentation" +msgstr "Segmentation de liste" + +#: 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 "" +"Envoyer des messages aux abonnés correspondant à des règles de segmentation " +"prédéfinies. Nul besoin de créer plusieurs listes présentant peu de " +"différences." + +#: views/index.hbs:11 +msgid "RSS Campaigns" +msgstr "Campagnes 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 "" +"Configurer Mailtrain pour surveiller des flux RSS, et, dès qu'une nouvelle " +"entrée est détectée dans un flux, auto-générer une nouvelle campagne en " +"utilisant les donnée de la nouvelle entrée comme contenu du message, et " +"l'envoyer aux abonnés sélectionnés" + +#: views/index.hbs:14 +msgid "GPG Encryption" +msgstr "Chiffrement 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 une liste est paramétrée avec un champ personnalisé pour les clés " +"publiques GPG, alors les utilisateurs pourront télécharger la leur pour " +"recevoir les messages sous forme chiffrée." + +#: views/index.hbs:17 +msgid "Click Stats" +msgstr "Statistiques de clics" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" +"Après l'envoi d'une campagne, consultez les statistiques individuelles de " +"clics pour chaque lien inclus dans le message." + +#: views/index.hbs:20 +msgid "Template Editors" +msgstr "Éditeur de modèle" + +#: 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 inclut GrapeJS et Mosaico, deux éditeurs de templates avancés. " +"Mailtrain offre également un éditeur de code si vous préférez créer le HTML " +"par vous-même." + +#: views/index.hbs:23 +msgid "Send via Any Provider" +msgstr "Envoyer via Any Provider" + +#: views/index.hbs:24 +msgid "" +"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 "" +"Vous pouvez utiliser n'importe quel fournisseur supportant le protocole SMTP " +"pour envoyer votre newletter. Le traitement des rejets et retours via appels " +"web est supporté avec SES, SparkPost, SendGrid et Mailgun, ainsi qu'avec " +"Postfix et ZoneMTA." + +#: views/index.hbs:26 lib/tools.js:137 +msgid "Automation" +msgstr "Automatisation" + +#: views/index.hbs:27 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" +"Définissez des triggers pour envoyer des messages spécifiques lorsqu'une " +"action utilisateur active le déclencheur." + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "Basculer la navigation" + +#: 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 "Compte" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "Configuration" + +#: 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 "Déconnexion" + +#: 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 "Se connecter" + +#: views/layout.hbs:11 +msgid "Self Hosted Newsletter App Built on Top of Nodemailer" +msgstr "Solution de Newsletter auto-hébergée construite autour de Nodemailer" + +#: views/layout.hbs:12 views/layout.hbs:14 +msgid "Source on GitHub" +msgstr "Source sur GitHub" + +#: views/layout.hbs:13 +msgid "Subscribe to Our Newsletter" +msgstr "S'abonner à notre lettre d'information" + +#: 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 "Listes" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:13 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "Créer une liste" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "Nom de la liste" + +#: views/lists/create.hbs:9 views/lists/edit.hbs:15 +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "Abonnement" + +#: views/lists/create.hbs:10 views/lists/edit.hbs:16 +msgid "Allow public users to subscribe themselves" +msgstr "Permettre à tous les utilisateurs de s'abonner par eux-mêmes" + +#: views/lists/create.hbs:11 views/lists/edit.hbs:17 +msgid "Unsubscription" +msgstr "Désabonnement" + +#: views/lists/create.hbs:12 views/lists/edit.hbs:18 +msgid "Select how an unsuscription request by subscriber is handled." +msgstr "" +"Choisissez comment une demande de désabonnement doit être prise en compte." + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:8 +msgid "Edit List" +msgstr "Éditer la liste" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "Voir la liste" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "Identifiant de liste" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "Il s'agit de l'identifiant de liste présenté aux utilisateurs" + +#: views/lists/edit.hbs:12 +msgid "Custom Form" +msgstr "Formulaire personnalisé" + +#: views/lists/edit.hbs:13 views/lists/forms/forms.hbs:11 +msgid "Default Mailtrain Form" +msgstr "Formulaire de Mailtrain par défaut" + +#: views/lists/edit.hbs:14 +msgid "" +"The custom form used for this list. You can create a form here." +msgstr "" +"Le formulaire personnalisé utilisé pour cette liste. Vous pouvez créer un " +"formulaire ici." + +#: views/lists/edit.hbs:19 +msgid "Unsubscribe Header" +msgstr "Entête de désabonnement" + +#: views/lists/edit.hbs:20 +msgid "Do not send List-Unsubscribe headers" +msgstr "Ne pas envoyer les entêtes List-Unsubscribe" + +#: views/lists/edit.hbs:21 +msgid "Delete List" +msgstr "Supprimer la liste" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "Créer un champ" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "Créer un champ personnalisé" + +#: 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 "Nom du champ" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "Type de champ" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "Texte" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "Nombre" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "Site web" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "Clé publique GPG" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "Texte multi-lignes" + +#: 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 "Date" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "Date (MM/JJ/AAAA)" + +#: views/lists/fields/create.hbs:17 views/lists/fields/edit.hbs:18 +#: lib/models/fields.js:26 +msgid "Date (DD/MM/YYYY)" +msgstr "Date (JJ/MM/AAAA)" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "Date de naissance" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "Date de naissance (MM/JJ)" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "Date de naissance (JJ/MM)" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "Groupé" + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "Menus déroulants" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "Boutons radio" + +#: views/lists/fields/create.hbs:24 views/lists/fields/edit.hbs:25 +#: lib/models/fields.js:23 +msgid "Checkboxes" +msgstr "Cases à cocher" + +#: views/lists/fields/create.hbs:25 views/lists/fields/edit.hbs:26 +msgid "Option for a group value" +msgstr "Option pour un groupe de valeurs" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "Groupe" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "Options de groupe requis" + +#: 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 "Valeur par défaut pour ce champ de publipostage" + +#: 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 "" +"Pour les éléments groupés comme les cases à cocher, vous pouvez contrôler " +"l'apparence du champ de publipostage avec une template optionnelle. La " +"template utilise la syntaxe Handlebars et vous trouvez les valeurs dans le " +"tableau {{values}}, par exemple {{#each values}} {{this}} " +"{{/each}}. Si aucune template n'est définie, alors les valeurs sont " +"concaténnées avec des virgules. Vous pouvez également utiliser la template " +"pour présenter des valeurs JSON (si le JSON est un tableau, alors le tableau " +"est accessible dans values, sinon vous accédez aux clés JSON " +"directement)." + +#: 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 "Ajouter un champ" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "Modifier le champ" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "Modifier le champ personnalisé" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "Retour aux champs" + +#: 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 "Champ de publipostage" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "Champ de publipostage" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "Utilisez ce mot-clé dans votre contenu :" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "Supprimer le champ" + +#: views/lists/fields/fields.hbs:7 views/lists/view.hbs:26 +#: views/report-templates/partials/report-template-fields.hbs:5 +msgid "Type" +msgstr "Type" + +#: 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:277 +#: routes/campaigns.js:569 routes/campaigns.js:658 routes/campaigns.js:707 +#: routes/lists.js:170 routes/lists.js:257 routes/report-templates.js:51 +#: routes/templates.js:183 routes/triggers.js:345 +msgid "Edit" +msgstr "Modifier" + +#: 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 "Formulaires personnalisés" + +#: views/lists/forms/create.hbs:4 +msgid "Create Form" +msgstr "Créer un formulaire" + +#: views/lists/forms/create.hbs:5 views/lists/forms/forms.hbs:4 +msgid "Create Custom Form" +msgstr "Créer un formulaire personnalisé" + +#: 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 "Nom du formulaire" + +#: views/lists/forms/create.hbs:8 +msgid "Add Form" +msgstr "Ajouter un formulaire" + +#: views/lists/forms/edit.hbs:4 +msgid "Edit Form" +msgstr "Modifier le formulaire" + +#: views/lists/forms/edit.hbs:5 +msgid "Edit Custom Form" +msgstr "Modifier le formulaire personnalisé" + +#: views/lists/forms/edit.hbs:6 +msgid "Back to forms" +msgstr "Retour aux formulaires" + +#: views/lists/forms/edit.hbs:10 +msgid "Optional comments about this form" +msgstr "Commentaire optionnel sur ce formulaire" + +#: views/lists/forms/edit.hbs:11 +msgid "Form Preview" +msgstr "Prévisualisation du formulaire" + +#: 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 "" +"NB : Ces liens sont fournis uniquement pour un avoir aperçu rapide. Si vous " +"utilisez ces formulaires vous serez redirigés vers les formulaires par " +"défaut." + +#: 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 "Abonnement" + +#: views/lists/forms/edit.hbs:14 +msgid "Confirm Subscription Notice" +msgstr "Confirmation d'abonnement" + +#: views/lists/forms/edit.hbs:15 +msgid "Confirm Unsubscription Notice" +msgstr "Confirmation de désabonnement" + +#: views/lists/forms/edit.hbs:16 +msgid "Subscribed Notice" +msgstr "Confirmation d'abonnement" + +#: views/lists/forms/edit.hbs:17 +msgid "Updated Notice" +msgstr "Notification enregistrée" + +#: views/lists/forms/edit.hbs:18 +msgid "Unsubscribed Notice" +msgstr "Notification de désabonnement" + +#: views/lists/forms/edit.hbs:19 +msgid "Manual Unsubscribe Notice" +msgstr "Notification de désabonnement manuel" + +#: views/lists/forms/edit.hbs:21 routes/forms.js:205 +msgid "Manage" +msgstr "Gérer" + +#: views/lists/forms/edit.hbs:22 +msgid "Manage Address" +msgstr "Gestion des adresses" + +#: views/lists/forms/edit.hbs:23 +msgid "Create a test user for additional options" +msgstr "Créer un utilisateur test pour les options additionnelles" + +#: 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 "Modèles" + +#: views/lists/forms/edit.hbs:25 +msgid "Fields" +msgstr "Champs" + +#: views/lists/forms/edit.hbs:27 +msgid "Form Fields" +msgstr "Champs de formulaire" + +#: views/lists/forms/edit.hbs:28 +msgid "Fields hidden on subscription page:" +msgstr "Champs masqués sur la page d'abonnement :" + +#: views/lists/forms/edit.hbs:29 +msgid "Fields shown on subscription page:" +msgstr "Champs affichés sur la page d'abonnement :" + +#: views/lists/forms/edit.hbs:30 +msgid "Fields hidden on preferences page:" +msgstr "Champs masqués sur la page de préférences :" + +#: views/lists/forms/edit.hbs:31 +msgid "Fields shown on preferences page:" +msgstr "Champs affichés sur la page de préférences :" + +#: views/lists/forms/edit.hbs:32 +msgid "Delete Form" +msgstr "Supprimer le formulaire" + +#: views/lists/forms/forms.hbs:10 +msgid "The default form for this list is:" +msgstr "Le formulaire par défaut pour cette liste est :" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "ID" + +#: views/lists/lists.hbs:7 views/reports/partials/report-fields.hbs:10 +msgid "Subscribers" +msgstr "Abonnés" + +#: 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 "Segments" + +#: 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 "Créer un segment" + +#: 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 "Nom du segment" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "Correspondance des règles" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "Toutes les règles doivent correspondre" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "Au moins une des règles doit correspondre" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "Ajouter un segment" + +#: 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 "Modifier le segment" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "Retour aux segments" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "Supprimer le segment" + +#: 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 "Créer une règle" + +#: 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 "Règle" + +#: views/lists/segments/rule-configure.hbs:7 +#: views/lists/segments/rule-edit.hbs:7 +msgid "Condition" +msgstr "Condition" + +#: views/lists/segments/rule-configure.hbs:8 +#: views/lists/segments/rule-edit.hbs:8 +msgid "Equals" +msgstr "vaut" + +#: views/lists/segments/rule-configure.hbs:9 +#: views/lists/segments/rule-edit.hbs:9 +msgid "Not equals" +msgstr "Différent" + +#: views/lists/segments/rule-configure.hbs:10 +#: views/lists/segments/rule-configure.hbs:11 +#: views/lists/segments/rule-configure.hbs:13 +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-configure.hbs:28 +#: views/lists/segments/rule-configure.hbs:33 +#: views/lists/segments/rule-edit.hbs:10 views/lists/segments/rule-edit.hbs:11 +#: views/lists/segments/rule-edit.hbs:13 views/lists/segments/rule-edit.hbs:18 +#: views/lists/segments/rule-edit.hbs:32 views/lists/segments/rule-edit.hbs:37 +#: views/lists/segments/view.hbs:11 +msgid "Value" +msgstr "Valeur" + +#: views/lists/segments/rule-configure.hbs:12 +#: views/lists/segments/rule-edit.hbs:12 +msgid "" +"Use % for wildcard character, e.g. \"%test\" to match all values that end " +"with \"test\"" +msgstr "" +"Utiliser % comme caractère joker, par ex. \"%test\" correspond à toutes les " +"valeurs qui finissent par \"test\"" + +#: views/lists/segments/rule-configure.hbs:14 +#: views/lists/segments/rule-configure.hbs:17 +#: 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:33 +msgid "Use exact match" +msgstr "Correspondance exacte" + +#: views/lists/segments/rule-configure.hbs:15 +#: views/lists/segments/rule-configure.hbs:18 +#: views/lists/segments/rule-configure.hbs:30 +#: views/lists/segments/rule-edit.hbs:15 views/lists/segments/rule-edit.hbs:20 +#: views/lists/segments/rule-edit.hbs:34 +msgid "Use range match" +msgstr "Correspondance par plage" + +#: views/lists/segments/rule-configure.hbs:19 +#: views/lists/segments/rule-edit.hbs:23 +msgid "Use relative range match" +msgstr "Correspondance par plages relatives" + +#: views/lists/segments/rule-configure.hbs:20 +#: views/lists/segments/rule-configure.hbs:31 +#: views/lists/segments/rule-edit.hbs:16 views/lists/segments/rule-edit.hbs:21 +#: views/lists/segments/rule-edit.hbs:24 views/lists/segments/rule-edit.hbs:35 +msgid "From" +msgstr "De" + +#: views/lists/segments/rule-configure.hbs:21 +#: views/lists/segments/rule-configure.hbs:25 +#: views/lists/segments/rule-edit.hbs:25 views/lists/segments/rule-edit.hbs:29 +msgid "days" +msgstr "jours" + +#: views/lists/segments/rule-configure.hbs:22 +#: views/lists/segments/rule-configure.hbs:26 +#: views/lists/segments/rule-edit.hbs:26 views/lists/segments/rule-edit.hbs:30 +msgid "before today" +msgstr "avant aujourd'hui" + +#: views/lists/segments/rule-configure.hbs:23 +#: views/lists/segments/rule-configure.hbs:27 +#: views/lists/segments/rule-edit.hbs:27 views/lists/segments/rule-edit.hbs:31 +msgid "after today" +msgstr "après aujourd'hui" + +#: views/lists/segments/rule-configure.hbs:24 +#: views/lists/segments/rule-configure.hbs:32 +#: views/lists/segments/rule-edit.hbs:17 views/lists/segments/rule-edit.hbs:22 +#: views/lists/segments/rule-edit.hbs:28 views/lists/segments/rule-edit.hbs:36 +msgid "to" +msgstr "à" + +#: views/lists/segments/rule-configure.hbs:34 +#: views/lists/segments/rule-edit.hbs:38 lib/models/segments.js:156 +#: lib/models/segments.js:419 +msgid "Selected" +msgstr "Sélectionné" + +#: views/lists/segments/rule-configure.hbs:35 +#: views/lists/segments/rule-edit.hbs:39 lib/models/segments.js:156 +#: lib/models/segments.js:419 +msgid "Not selected" +msgstr "Non sélectionné" + +#: views/lists/segments/rule-configure.hbs:36 +msgid "Add Rule" +msgstr "Ajouter une règle" + +#: views/lists/segments/rule-create.hbs:8 +#: views/lists/subscription/import.hbs:18 +#: views/reports/create-select-template.hbs:5 +#: views/triggers/create-select.hbs:9 +msgid "Next" +msgstr "Suivant" + +#: views/lists/segments/rule-edit.hbs:40 +msgid "Delete Rule" +msgstr "Supprimer la règle" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "Correspondance" + +#: views/lists/segments/view.hbs:5 views/lists/view.hbs:13 +msgid "Segment" +msgstr "Segment" + +#: views/lists/segments/view.hbs:7 +msgid "Match rules" +msgstr "Règles de correspondance" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "Abonnés en correspondance" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "voir" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "Ajouter un abonné" + +#: views/lists/subscription/add.hbs:5 +#: views/subscription/partials/subscription-custom-fields.hbs:1 +#: views/users/account.hbs:7 +msgid "Email Address" +msgstr "Adresse mail" + +#: views/lists/subscription/add.hbs:8 views/lists/subscription/edit.hbs:9 +#: views/settings.hbs:85 views/settings.hbs:100 +#: views/subscription/partials/subscription-custom-fields.hbs:6 +msgid "Begins with" +msgstr "qui commence par" + +#: 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 "" +"Insérez une clé publique GPG pour chiffrer les messages envoyés à cet abonné" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "Fuseau horaire" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "Utilisateur pour tests ?" + +#: 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 activé, cet abonnement peut être utilisé pour prévisualiser les message " +"des campagnes" + +#: 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 "" +"Cette personne ne recevra pas de mail de confirmation, aussi assurez-vous " +"que vous avez le droit de lui envoyer des messages." + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "Modifier l'abonné" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "Retour à la liste" + +#: 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 "Adresse mail" + +#: views/lists/subscription/edit.hbs:17 +msgid "Delete Subscription" +msgstr "Supprimer l'abonnement" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "Statut de l'import" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "Adresses en erreur" + +#: 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 "" +"Les adresse administratives comme postmaster@example.com sont supprimées " +"lors des imports. Les utilisateurs disposant d'adresses administrativves " +"doivent utiliser le formulaire d'abonnement" + +#: views/lists/subscription/import-failed.hbs:7 +msgid "see here" +msgstr "voir ici" + +#: views/lists/subscription/import-failed.hbs:9 +msgid "Fail reason" +msgstr "Raison de l'échec" + +#: 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 "Importer des abonnés" + +#: views/lists/subscription/import-preview.hbs:10 views/users/api.hbs:21 +#: views/users/api.hbs:36 views/users/api.hbs:44 views/users/api.hbs:52 +#: views/users/api.hbs:64 views/users/api.hbs:75 views/users/api.hbs:83 +#: views/users/api.hbs:91 views/users/api.hbs:96 views/users/api.hbs:101 +#: views/users/api.hbs:106 +msgid "Example" +msgstr "Exemple" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "Lancer l'importation" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "Fichier CSV" + +#: views/lists/subscription/import.hbs:6 +msgid "The first line must contain column headings" +msgstr "La première ligne doit contenir les entêtes de colonnes" + +#: views/lists/subscription/import.hbs:7 +msgid "CSV delimiter" +msgstr "Délimiteur CSV" + +#: views/lists/subscription/import.hbs:8 +msgid "Categorize the imported subscribers as" +msgstr "Catégoriser les abonnés importés comme" + +#: views/lists/subscription/import.hbs:9 routes/lists.js:206 +msgid "Subscribed" +msgstr "Abonné" + +#: views/lists/subscription/import.hbs:10 +msgid "Regular subscriber addresses" +msgstr "Abonnés ordinaires" + +#: views/lists/subscription/import.hbs:11 +msgid "Subscribed (Force)" +msgstr "Abonné (forcé)" + +#: views/lists/subscription/import.hbs:12 +msgid "Regular subscriber addresses, resubscribe users that have unsubscribed" +msgstr "" +"Abonnés ordinaires, et réabonner les utilisateurs qui s'étaient désabonnés" + +#: views/lists/subscription/import.hbs:14 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "Désabonnements. Les adresses seront désabonnées de la liste" + +#: views/lists/subscription/import.hbs:15 +msgid "Check imported emails" +msgstr "Vérifier les adresses mail importées" + +#: views/lists/subscription/import.hbs:16 views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "Activé" + +#: views/lists/subscription/import.hbs:17 views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "Désactivé" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "Formulaire d'abonnement" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "Actions sur la liste" + +#: 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 "Créer un déclencheur" + +#: views/lists/view.hbs:10 +msgid "Add Subscriber" +msgstr "Ajouter un abonné" + +#: views/lists/view.hbs:11 +msgid "Import Subscribers" +msgstr "Importer des abonnés" + +#: views/lists/view.hbs:15 +msgid "Create New Segment" +msgstr "Créer un nouveau segment" + +#: views/lists/view.hbs:16 +msgid "Filter" +msgstr "Filtre" + +#: views/lists/view.hbs:17 +msgid "Subscriptions" +msgstr "Abonnements" + +#: views/lists/view.hbs:18 +msgid "Imports" +msgstr "Imports" + +#: views/lists/view.hbs:25 routes/campaigns.js:256 routes/lists.js:300 +msgid "Finished" +msgstr "Terminé" + +#: views/lists/view.hbs:27 +msgid "Added" +msgstr "Ajouté" + +#: views/lists/view.hbs:28 +msgid "Updated" +msgstr "Mis à jour" + +#: views/lists/view.hbs:29 +msgid "Failed" +msgstr "Echec" + +#: views/lists/view.hbs:31 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" +"Êtes-vous sûr ? Cette action ne doit être appelée que pour interrompre les " +"imports bloqués" + +#: views/lists/view.hbs:32 +msgid "Restart" +msgstr "Redémarrer" + +#: views/mosaico/editor.hbs:1 views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "Lexique des champs de publipostage" + +#: views/mosaico/editor.hbs:4 +msgid "MOSAICO Responsive Email Designer" +msgstr "Editeur de mail responsive MOSAICO" + +#: views/mosaico/editor.hbs:5 +msgid "Sucessfully saved" +msgstr "Enregistré" + +#: views/mosaico/editor.hbs:6 +msgid "An error occured while saving the document" +msgstr "Une erreur est survenue lors de l\\'enregistrement du document" + +#: views/mosaico/editor.hbs:7 +msgid "Unsaved changes will be lost. Close now?" +msgstr "" +"Les éventuelles modifications non sauvegardées seront perdues. Fermer " +"maintenant ?" + +#: views/mosaico/editor.hbs:8 views/mosaico/editor.hbs:9 +msgid "Tags" +msgstr "Champs de publipostage" + +#: 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 "Contenu du modèle (HTML)" + +#: views/partials/editor-navbar.hbs:1 +msgid "SAVE" +msgstr "ENREGISTRER" + +#: views/partials/editor-navbar.hbs:2 +msgid "SAVING" +msgstr "ENREGISTRE" + +#: views/partials/editor-navbar.hbs:3 +msgid "CLOSE" +msgstr "FERMER" + +#: views/partials/grapejs.hbs:2 +msgid "Open GrapeJS" +msgstr "Ouvrir GrapeJS" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "Basculer la prévisualisation HTML" + +#: views/partials/html-to-text.hbs:1 +msgid "" +"To extract the text from HTML click here." +msgstr "" +"Pour extraire le texte du HTML, cliquez ici." + +#: 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 "" +"Notez bien que le texte présent dans le champ ci-dessus sera remplacé. Cette " +"fonctionnalité utilisePremailer API, un service tiers. Merci de " +"vérifier leurs Conditions générales d'utilisation et de vie privée, qui " +"s'appliquent en ce cas." + +#: views/partials/html-to-text.hbs:3 +msgid "An error occurred while talking to the server" +msgstr "Une erreur est intervenue lors du dialogue avec le serveur" + +#: 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 "" +"Les champs de publipostage sont des champs qui sont remplacés avant l'envoi " +"du message. Le format du champ de publipostage est le suivant : " +"[ETIQUETTE] ou [ETIQUETTE/defaut]defaut est une valeur textuelle optionnelle utilisée lorsque ETIQUETTE est vide." + +#: views/partials/modal-carousel.hbs:1 +msgid "{{title}}" +msgstr "{{title}}" + +#: views/partials/mosaico.hbs:2 +msgid "Open Mosaico" +msgstr "Ouvrir Mosaico" + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "Contenu du modèle (Texte)" + +#: 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 "Rapports" + +#: 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 "Créer un modèle" + +#: views/report-templates/create.hbs:5 routes/report-templates.js:231 +msgid "Create Report Template" +msgstr "Créer un modèle de rapport" + +#: views/report-templates/edit.hbs:4 views/templates/edit.hbs:3 +#: views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "Modifier le modèle" + +#: views/report-templates/edit.hbs:5 routes/report-templates.js:262 +msgid "Edit Report Template" +msgstr "Modifier le modèle de rapport" + +#: views/report-templates/edit.hbs:6 views/templates/edit.hbs:12 +msgid "Delete Template" +msgstr "Supprimer le modèle" + +#: views/report-templates/edit.hbs:7 +msgid "Update and Stay" +msgstr "Mettre à jour et poursuivre" + +#: views/report-templates/edit.hbs:8 +msgid "Update and Leave" +msgstr "Mettre à jour et quitter" + +#: views/report-templates/partials/report-template-fields.hbs:2 +msgid "Template Name" +msgstr "Nom du modèle" + +#: views/report-templates/partials/report-template-fields.hbs:6 +msgid "User selectable fields" +msgstr "Champs utilisateur utilisables" + +#: views/report-templates/partials/report-template-fields.hbs:7 +msgid "Data processing code" +msgstr "Code de traitement des données" + +#: views/report-templates/partials/report-template-fields.hbs:8 +msgid "Rendering template" +msgstr "Modèle de rendu" + +#: views/report-templates/report-templates.hbs:5 +msgid "Blank" +msgstr "Vide" + +#: views/report-templates/report-templates.hbs:6 +msgid "All Subscribers" +msgstr "Tous les abonnés" + +#: views/report-templates/report-templates.hbs:7 +msgid "Grouped Subscribers" +msgstr "Abonnements groupés" + +#: views/report-templates/report-templates.hbs:8 +msgid "Export List as CSV" +msgstr "Exporter la liste au format CSV" + +#: views/report-templates/report-templates.hbs:9 views/reports/reports.hbs:4 +#: routes/report-templates.js:29 +msgid "Report Templates" +msgstr "Modèles de rapport" + +#: 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 "Créer un rapport" + +#: views/reports/edit.hbs:3 views/reports/edit.hbs:4 routes/reports.js:151 +msgid "Edit Report" +msgstr "Modifier le rapport" + +#: views/reports/edit.hbs:5 +msgid "Delete Report" +msgstr "Supprimer le rapport" + +#: views/reports/partials/report-fields.hbs:2 +msgid "Report Name" +msgstr "Nom du rapport" + +#: 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 "" +"Sélectionnez une campagne dans la table ci-dessus en cliquant sur son numéro " +"de ligne." + +#: views/reports/partials/report-select-template.hbs:1 +msgid "Report Template" +msgstr "Modèle de rapport" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "Adresse du service (URL)" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "Entrez l'URL à laquelle ce service peut être joint" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "Email de l'administrateur" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "Entrez l'adresse mail d'émission des messages système" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "Désactiver l'éditeur WYSIWYG" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "" +"Si activé, l'editeur HTML affiche le code HTML sans la prévisualisation" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "Désactiver les messages de confirmation d'abonnement" + +#: 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 activé, aucun message de confirmation suivant lequel un utilisateur a été " +"abonné ou désabonné ne sera envoyé. Cela ne désactive pas les messages de " +"confirmation d'abonnement." + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "Identifiant de suivi" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "Entrez le code de suivi Google Analytics" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "Annonce de la page d'accueil" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "Code HTML présent sur l'entête de la page d'accueil" + +#: views/settings.hbs:17 +msgid "X-Mailer header" +msgstr "Entête X-Mailer" + +#: views/settings.hbs:18 +msgid "Set a custom X-Mailer header value or leave empty to disable it" +msgstr "" +"Renseignez un entête X-Mailer personnalisé, et laissez vide pour désactiver" + +#: views/settings.hbs:19 +msgid "Campaign defaults" +msgstr "Paramètres par défaut de la campagne" + +#: views/settings.hbs:20 +msgid "Sender name" +msgstr "Nom de l'émetteur" + +#: views/settings.hbs:21 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "Nom de l'émetteur, par ex. My Awesome Company Ltd." + +#: views/settings.hbs:22 +msgid "Default address" +msgstr "Adresse mail par défaut" + +#: views/settings.hbs:23 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "" +"Adresse de contact à fournir, par ex. 1234 Main Street, Anywhere, MA 01234, " +"USA" + +#: views/settings.hbs:24 +msgid "Default \"from name\"" +msgstr "Nom d'émetteur par défaut" + +#: views/settings.hbs:26 +msgid "Default \"from\" email" +msgstr "Adresse mail d'émetteur par défaut" + +#: views/settings.hbs:28 +msgid "Default \"subject line\"" +msgstr "Objet du mail par défaut" + +#: views/settings.hbs:30 +msgid "Default homepage (URL)" +msgstr "Page d'accueil par défaut (URL)" + +#: views/settings.hbs:31 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "URL où rediriger les utilisateurs abonnés, par ex. http://example.com/" + +#: views/settings.hbs:32 +msgid "Default custom unsubscribe (URL))" +msgstr "URL de désabonnement personnalisable par défaut" + +#: views/settings.hbs:33 +msgid "Custom unsubscribe URL, eg. http://example.com/unsubscribe/[EMAIL]" +msgstr "" +"URL personnalisée de désabonnement, par ex. http://example.com/unsubscribe/" +"[EMAIL]" + +#: views/settings.hbs:34 +msgid "Set a custom unsubscribe url." +msgstr "Renseignez une URL de désabonnement personnalisée." + +#: views/settings.hbs:35 +msgid "Mailer Settings" +msgstr "Paramètres mailer" + +#: views/settings.hbs:36 +msgid "These settings are required to send out e-mail messages" +msgstr "Ces paramètres sont obligatoires pour envoyer des messages" + +#: views/settings.hbs:37 +msgid "SMTP" +msgstr "SMTP" + +#: views/settings.hbs:38 +msgid "AWS SES" +msgstr "AWS SES" + +#: views/settings.hbs:39 +msgid "Use SMTP for sending mail" +msgstr "Utiliser SMTP pour envoyer les mails" + +#: views/settings.hbs:40 +msgid "Hostname" +msgstr "Nom de machine" + +#: views/settings.hbs:41 +msgid "Port" +msgstr "Port" + +#: views/settings.hbs:42 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "Port, par ex. 465. Autodétecté si vide" + +#: views/settings.hbs:43 +msgid "Encryption" +msgstr "Chiffrement" + +#: views/settings.hbs:44 +msgid "Disable SMTP authentication" +msgstr "Désactiver l'authentification SMTP" + +#: views/settings.hbs:45 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "Utilisateur" + +#: views/settings.hbs:46 +msgid "Username, eg. myaccount@example.com" +msgstr "Nom d'utilisateur, par ex. myaccount@example.com" + +#: views/settings.hbs:47 views/settings.hbs:48 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "Mot de passe" + +#: views/settings.hbs:49 +msgid "Use SES API for sending mail" +msgstr "Utiliser l'API SES pour envoyer les mails" + +#: views/settings.hbs:50 +msgid "Access Key" +msgstr "Clé d'accès" + +#: views/settings.hbs:51 +msgid "AWS Access Key Id" +msgstr "ID de la clé d'accès AWS" + +#: views/settings.hbs:52 +msgid "Secret Key" +msgstr "Clé secrète" + +#: views/settings.hbs:53 +msgid "AWS Secret Access Key" +msgstr "Secret de la clé d'accès AWS" + +#: views/settings.hbs:54 +msgid "Region" +msgstr "Région" + +#: views/settings.hbs:55 +msgid "Checking" +msgstr "Vérification" + +#: views/settings.hbs:56 +msgid "Check Mailer config" +msgstr "Vérifier la configuration du serveur mail" + +#: views/settings.hbs:57 +msgid "Advanced Mailer settings" +msgstr "Paramètres avancés du serveur mail" + +#: views/settings.hbs:58 +msgid "Log SMTP transactions" +msgstr "Log des transactions SMTP" + +#: views/settings.hbs:59 +msgid "Allow self-signed certificates" +msgstr "Autoriser les certificats autosignés" + +#: views/settings.hbs:60 +msgid "Max connections" +msgstr "Nombre maximum de connections" + +#: views/settings.hbs:61 +msgid "The count of max connections, eg. 10" +msgstr "Le nombre maximum de connexions, par ex. 10" + +#: views/settings.hbs:62 +msgid "" +"The count of maximum simultaneous connections to make against the SMTP " +"server (defaults to 5). This limit is per sending process." +msgstr "" +"Le nombre maximum de connexions simultannées vers le serveur SMTP (par " +"défaut 5). Cette limite s'entend par process d'envoi" + +#: views/settings.hbs:63 +msgid "Max messages" +msgstr "Nombre maximum de messages" + +#: views/settings.hbs:64 +msgid "The count of max messages, eg. 100" +msgstr "Le nombre maximum de messages, par ex. 100" + +#: views/settings.hbs:65 +msgid "" +"The number of messages to send through a single connection before the " +"connection is closed and reopened (defaults to 100)" +msgstr "" +"Le nombre de messages à envoyer au travers d'une connexion unique avant " +"qu'elle ne soit fermée puis réouverte (par défaut 100)" + +#: views/settings.hbs:66 +msgid "Throttling" +msgstr "Restrictions" + +#: views/settings.hbs:67 +msgid "Messages per hour eg. 1000" +msgstr "Messages par heure ex: 1000" + +#: views/settings.hbs:68 +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 "" +"Nombre maximum de messages envoyés par heure. Laissez vide pour ne pas " +"restreindre. Si votre fournisseur utilise une unité différente (messages/" +"minute ou messages/seconde) convertissez cette limite en messages/heure (1 m/" +"s => 3600 m/h). Cette limite se comprend par process d'émission." + +#: views/settings.hbs:69 +msgid "VERP bounce handling" +msgstr "Traitement VERP des rejets" + +#: views/settings.hbs:70 +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 peut utiliser un routage VERP de façon à détecter les adresses en " +"erreur. Pour cela le message est envoyé avec un adresse de retour VERP " +"spécifique. Si le message est rejeté, un message de rejet est renvoyé à " +"cette adresse VERP, permettant de détecter l'adresse en erreur." + +#: views/settings.hbs:71 +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 "" +"Pour que le VERP fonctionne, vous devez renseigner un enregistrement DNS MX " +"qui pointe vers le nom de domaine de votre Mailtrain. Vérifiez également que " +"l'interface VERP est disponible sur le port 25 de votre serveur (le port 25 " +"nécessite habituellement les privilèges root). Si quelqu'un envoie un mail à " +"someuser@verp-hostname, le mail doit atteindre ce serveur." + +#: views/settings.hbs:72 +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 "" +"Le VERP ne fonctionne habituellement que si vous utilisez votre propre " +"serveur SMTP. Les services de relai ordinaires (SES, SparkPost, Gmail, etc.) " +"tendent à supprimer les adresses VERP des messages." + +#: views/settings.hbs:73 +msgid "Use VERP to catch bounces" +msgstr "Utiliser le VERP pour récupérer les rejets" + +#: views/settings.hbs:74 +msgid "Server hostname" +msgstr "Nom de machine du serveur" + +#: views/settings.hbs:75 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "Le nom du serveur VERP, par ex. bouces.example.com" + +#: views/settings.hbs:76 +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 "" +"Nom du serveur de traitement VERP. Ce nom d'hôte est habituellement utilisée " +"dans l'entête SMTP FROM d'enveloppe, et est pointé par l'enregistrement DNS " +"MX." + +#: views/settings.hbs:77 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" +"Le serveur de traitement VERP n'est pas activé. Modifier le fichier de " +"configuration et redémarrer le serveur pour l'activer" + +#: views/settings.hbs:78 +msgid "GPG Signing" +msgstr "Signature GPG" + +#: views/settings.hbs:79 +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 "" +"Seuls les messages chiffrés peuvent être signés. Les abonnés qui n'ont pas " +"fourni dans leur profil de clé publique GPG reçoivent normalement les mails. " +"Les utilisateurs qui ont fourni leur clé GPG recevront les mails sous forme " +"chiffrée, et si vous avez fourni une clé de signature, les messages seront " +"en plus signés avec cette clé." + +#: views/settings.hbs:80 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" +"Ne pas utiliser de clé sensible ici. La clé privée et la phrase de " +"déchiffrement ne sont pas chiffrées dans la base de données." + +#: views/settings.hbs:81 +msgid "Private Key Passphrase" +msgstr "Phrase de déchiffrement de la clé privée" + +#: views/settings.hbs:82 +msgid "Passphrase for the key if set" +msgstr "Phrase de déchiffrement de la clé si fournie" + +#: views/settings.hbs:83 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "" +"Renseignez uniquement si votre clé privée est protégée par un mot de passe" + +#: views/settings.hbs:84 +msgid "GPG Private Key" +msgstr "Clé privée GPG" + +#: views/settings.hbs:86 +msgid "" +"This value is optional. If you do not provide a private key GPG encrypted " +"messages are sent without signing." +msgstr "" +"Cette valeur est optionnelle. Si vous ne fournissez pas de clé privé GPG, " +"les messages chiffrés sont envoyés sans signature." + +#: views/settings.hbs:87 +msgid "DKIM Signing by ZoneMTA" +msgstr "Signature DKIM par ZoneMTA" + +#: views/settings.hbs:88 +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 vous utilisez ZoneMTA, Mailtrain peut fournir une clé DKIM pour signer " +"les messages sortants. Les autres services fournissent habituellement leurs " +"propres moyens pour signer vos messages avec DKIM" + +#: views/settings.hbs:89 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" +"Ne pas utiliser de clé sensible ici. La clé privée n'est pas chiffrée dans " +"la base de données." + +#: views/settings.hbs:90 +msgid "ZoneMTA DKIM API Key" +msgstr "Clé de l'API DKIM ZoneMTA" + +#: views/settings.hbs:91 +msgid "Some secret value" +msgstr "Valeur secrète" + +#: views/settings.hbs:92 +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 "" +"Valeur secrète connue de ZoneMTA pour les requêtes d'informations de clés " +"DKIM. Si cette valeur a été générée lors de l'installation Mailtrain, elle " +"peut être conservée." + +#: views/settings.hbs:93 +msgid "DKIM domain" +msgstr "Domaine DKIM" + +#: views/settings.hbs:94 +msgid "Domain name for the DKIM key" +msgstr "Nom de domaine pour la clé DKIM" + +#: views/settings.hbs:95 +msgid "Leave blank to use the sender email address domain" +msgstr "Laissez vide pour utiliser le domaine de l'adresse mail de l'émetteur" + +#: views/settings.hbs:96 views/settings.hbs:97 +msgid "DKIM key selector" +msgstr "Sélecteur de clé DKIM" + +#: views/settings.hbs:98 +msgid "Signing is disabled without a valid selector value" +msgstr "L'abonnement est désactivé en l'absence de valeur de sélection valide" + +#: views/settings.hbs:99 +msgid "DKIM Private Key" +msgstr "Clé privée DKIM" + +#: views/settings.hbs:101 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" +"Cette valeur est optionnelle. Si vous ne fournissez pas de clé privée, les " +"messages ne seront pas signés." + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:1 +#: views/subscription/mail-already-subscribed-text.hbs:1 +#: lib/models/subscriptions.js:174 lib/models/subscriptions.js:895 +msgid "Email address already registered" +msgstr "Adresse mail déjà enregistrée" + +#: 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 "" +"Nous avons reçu une demande d'abonnement. Cependant votre adresse mail est " +"déjà enregistrée." + +#: 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 vous avez reçu ce message par erreur, vous pouvez simplement le " +"supprimer. Votre abonnement ne sera pas modifié." + +#: 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 vous voulez modifier votre abonnement vous pouvez" + +#: 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 "gestion de vos préférences" + +#: 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 "ou" + +#: 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 "désabonnement ici" + +#: 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 "Retour à notre site 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 "Pour les questions à ce sujet, contactez :" + +#: 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 vous voulez modifier votre abonnement vous pouvez :" + +#: 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 "Merci de confirmer votre changement d'adresse mail" + +#: 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 "Oui, abonner cette adresse mail à cette liste" + +#: 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 vous avez reçu ce message par erreur, vous pouvez simplement le " +"supprimer. Vous ne serez pas abonné tant que vous ne cliquez pas le lien ci-" +"dessus." + +#: 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 vous avez reçu ce message par erreur, vous pouvez simplement le " +"supprimer. Vous ne serez pas abonné tant que vous ne cliquez pas le lien ci-" +"dessus." + +#: 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:432 +msgid "Please Confirm Subscription" +msgstr "Merci de confirmer votre abonnement" + +#: 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 "Oui, je m'abonne à cette liste" + +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:1 +msgid "Please Confirm Unsubscription" +msgstr "Merci de confirmer votre désabonnement" + +#: 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 "Oui, je me désabonne de cette liste" + +#: 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 vous avez reçu ce message par erreur, vous pouvez simplement le " +"supprimer. Vous ne serez pas désabonné tant que vous ne cliquez pas le lien " +"ci-dessus." + +#: 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 vous avez reçu ce message par erreur, vous pouvez simplement le " +"supprimer. Vous ne serez pas abonné tant que vous ne cliquez pas le lien ci-" +"dessus." + +#: 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 "Abonnement confirmé" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed" +msgstr "Votre abonnement à la liste a été confirmé" + +#: 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 "Votre abonnement à la liste a été confirmé." + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:1 +msgid "You Are Now Unsubscribed" +msgstr "Vous êtes désormais désabonné" + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:2 +msgid "We have removed your email address from our list" +msgstr "Nous avons retiré votre adresse mail de notre liste" + +#: 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 vous vous êtes désabonné par erreur, vous pouvez à nouveau vous abonner " +"ici :" + +#: views/subscription/mail-unsubscription-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "Nous avons retiré votre adresse mail de notre liste." + +#: views/subscription/partials/subscription-custom-fields.hbs:2 +msgid "want to change it?" +msgstr "Voulez-vous le changer ?" + +#: views/subscription/partials/subscription-custom-fields.hbs:5 +msgid "Download signature verification key" +msgstr "Télécharger la clé de vérification de signature" + +#: views/subscription/partials/subscription-custom-fields.hbs:7 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "" +"Insérez ici votre clé publique GPG pour chiffrer les messages envoyés à " +"votre adresse" + +#: views/subscription/partials/subscription-custom-fields.hbs:8 +msgid "optional" +msgstr "optionnel" + +#: views/subscription/partials/subscription-flash-messages.hbs:1 +#: views/subscription/partials/subscription-flash-messages.hbs:3 +msgid "Warning!" +msgstr "Attention !" + +#: views/subscription/partials/subscription-flash-messages.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "" +"Si javascript n'était pas activé, aucun message de confirmation n'a été " +"envoyé" + +#: views/subscription/partials/subscription-flash-messages.hbs:4 +msgid "JavaScript must be enabled in order for this form to work" +msgstr "JavaScript doit être activé pour utiliser ce formulaire" + +#: views/subscription/partials/subscription-manage-address-form.hbs:1 +msgid "Existing Email Address" +msgstr "Adresse mail existante" + +#: views/subscription/partials/subscription-manage-address-form.hbs:2 +msgid "New Email Address" +msgstr "Nouvelle adresse mail" + +#: views/subscription/partials/subscription-manage-address-form.hbs:3 +msgid "Your new email address" +msgstr "Votre nouvelle adresse mail" + +#: 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 "" +"Vous allez recevoir une demande de confirmation à votre nouvelle adresse " +"mail, que vous devrez acceptez pour que votre nouvelle adresse soit prise en " +"compte" + +#: views/subscription/partials/subscription-manage-address-form.hbs:5 +#: views/subscription/web-manage-address.mjml.hbs:2 +msgid "Update Email Address" +msgstr "Mettre à jour l'adresse mail" + +#: views/subscription/partials/subscription-manage-form.hbs:1 +#: views/subscription/web-manage.mjml.hbs:2 +msgid "Update Profile" +msgstr "Mettre à jour le profil" + +#: 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 "S'abonner à la liste" + +#: 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 "Presque terminé" + +#: 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 "" +"Nous devons confirmer votre adresse mail. Pour terminer la procédure " +"d'abonnement, merci de cliquer sur le lien présent dans le mail que nous " +"venons de vous envoyer." + +#: 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 "" +"Nous devons confirmer votre adresse mail. Pour terminer la procédure de " +"désabonnement, merci de cliquer sur le lien présent dans le mail que nous " +"venons de vous envoyer." + +#: views/subscription/web-manage-address.mjml.hbs:1 +msgid "Update Your Email Address" +msgstr "Mettre à jour votre adresse mail" + +#: views/subscription/web-manage.mjml.hbs:1 +msgid "Update Your Preferences" +msgstr "Mettre à jour vos préférences" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:1 +msgid "Online Unsubscription Is Not Possible" +msgstr "Le désabonnement en ligne n'est pas autorisé" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:2 +msgid "Please contact us at" +msgstr "Merci de nous contacter à" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:3 +msgid "to get removed from the list" +msgstr "pour être retiré de la liste" + +#: views/subscription/web-subscribe.mjml.hbs:1 +msgid "Subscribe to List" +msgstr "S'abonner à la liste" + +#: views/subscription/web-subscribed-notice.mjml.hbs:3 +msgid "Thank you for subscribing!" +msgstr "Merci de votre abonnement !" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:1 +msgid "Unsubscribe Successful" +msgstr "Désabonnement pris en compte" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:2 +msgid "You have been removed from:" +msgstr "Vous avez été retiré de :" + +#: views/subscription/web-updated-notice.mjml.hbs:1 +msgid "Profile Updated" +msgstr "Profil mis à jour" + +#: views/subscription/web-updated-notice.mjml.hbs:2 +msgid "Your profile information has been updated." +msgstr "Vos informations de profil ont été enregistrées" + +#: views/subscription/widget-subscribe.hbs:2 +msgid "Sending ..." +msgstr "Envoi ..." + +#: views/subscription/widget-subscribe.hbs:3 +msgid "It looks like you are already subscribed to this list." +msgstr "Il semble que vous soyez déjà abonné à cette liste." + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "Nom du modèle" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "Nom de ce modèle, par ex. Newsletter" + +#: views/templates/create.hbs:7 +msgid "HTML Editor" +msgstr "Éditeur HTML" + +#: views/templates/create.hbs:10 views/templates/edit.hbs:9 +msgid "Optional comments about this template" +msgstr "Commentaire optionnel sur ce modèle" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "Retour aux modèles" + +#: views/templates/edit.hbs:11 +msgid "Duplicate" +msgstr "Dupliquer" + +#: 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 "Déclencheurs des automatismes" + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "Sélectionnez une liste pour le déclencheur" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "Nom du déclencheur" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "Nom de ce déclencheur, par ex. Abonnés inactifs" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "Commentaire optionnel sur ce déclencheur" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "Règle du déclencheur" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "Déclencheur lancé" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "jours suivants :" + +#: views/triggers/create.hbs:16 views/triggers/create.hbs:21 +#: views/triggers/edit.hbs:18 views/triggers/edit.hbs:23 +msgid "Event" +msgstr "Évènement" + +#: 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 "Campagne" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "Action du déclencheur" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "Envoyer la campagne" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "Modifier le déclencheur" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "Retour aux déclencheurs" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "Le déclencheur est activé" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "Supprimer le déclencheur" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "Déclenché" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "Abonnés déclenchés" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "Abonnés ayant activé ce déclencheur" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "Heure du déclenchement" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "Déclencheur" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "Campagne cible" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "Nombre de déclencheurs" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "Ce compte est géré par le LDAP." + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "Adresse mail associée" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "Votre adresse mail" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you lose your password" +msgstr "Cette adresse vous permet de réinitialiser un mot de passe perdu" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "Changement du mot de passe" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "" +"Vous avez uniquement ce formulaire à remplir pour changer votre mot de passe" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "Mot de passe actuel" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "Nouveau mot de passe" + +#: views/users/account.hbs:16 views/users/reset.hbs:8 +msgid "Confirm Password" +msgstr "Confirmation du mot de passe" + +#: views/users/account.hbs:17 views/users/reset.hbs:9 +msgid "Confirm New Password" +msgstr "Confirmez le nouveau mot de passe" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "Êtes-vous sûr ? La réinitialisation invaliderait les jetons existants." + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "Êtes-vous sûr ?" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "Réinitialiser le jeton d'accès" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "Générer un jeton d'accès" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "Jeton d'accès privé :" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "Le jeton d'accès n'est pas encore généré" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "Notes à propos de l'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 "" +"La réponse de l'API est un document JSON avec les propriétés error et data. Si la propriété error est " +"renseignée, alors la requête a échoué." + +#: 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 "" +"Vous devez définir le bon Content-Type lors de vos requêtes. " +"Vous pouvez utiliser soit application/x-www-form-urlencoded " +"pour des données de formulaire, ou application/json pour une " +"requête JSON. Vous ne pouvez pas utiliser multipart/form-data." + +#: views/users/api.hbs:13 +msgid "Get list of subscriptions" +msgstr "Obtenir la liste des abonnés" + +#: views/users/api.hbs:14 +msgid "" +"Retrieve a list of subscriptions to the list referenced by :listId. All " +"fields of the subscribers will be returned. Note that custom fields will " +"have generated names." +msgstr "" +"Récupérer l'ensemble des abonnements à la liste référencée :listId. Tous les " +"champs des abonnés seront renvoyés. Notez que les champs personnalisés " +"auront des noms générés automatiquement." + +#: views/users/api.hbs:15 views/users/api.hbs:24 views/users/api.hbs:26 +#: views/users/api.hbs:39 views/users/api.hbs:41 views/users/api.hbs:47 +#: views/users/api.hbs:49 views/users/api.hbs:55 views/users/api.hbs:57 +#: views/users/api.hbs:67 views/users/api.hbs:78 views/users/api.hbs:80 +#: views/users/api.hbs:86 views/users/api.hbs:88 views/users/api.hbs:94 +#: views/users/api.hbs:99 views/users/api.hbs:104 +msgid "arguments" +msgstr "arguments" + +#: views/users/api.hbs:16 views/users/api.hbs:25 views/users/api.hbs:40 +#: views/users/api.hbs:48 views/users/api.hbs:56 views/users/api.hbs:68 +#: views/users/api.hbs:79 views/users/api.hbs:87 views/users/api.hbs:95 +#: views/users/api.hbs:100 views/users/api.hbs:105 +msgid "your personal access token" +msgstr "votre token d'accès personnel" + +#: views/users/api.hbs:17 views/users/api.hbs:69 +msgid "Start position" +msgstr "Position de démarrage" + +#: views/users/api.hbs:18 views/users/api.hbs:70 +msgid "optional, default 0" +msgstr "optionnel, par défaut 0" + +#: views/users/api.hbs:19 +msgid "limit subscription count in response" +msgstr "nombre maximal d'abonnements retournés" + +#: views/users/api.hbs:20 views/users/api.hbs:72 +msgid "optional, default 10000" +msgstr "optionnel, par défaut 10000" + +#: views/users/api.hbs:22 +msgid "Add subscription" +msgstr "Ajouter un abonnement" + +#: views/users/api.hbs:23 +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 "" +"Ce service de l'API insère ou met à jour un abonnement. Les champs non " +"fournis sont laissés tels quels. Ainsi, pour un abonnement existant, si vous " +"ne mettez à jour que la valeur de LAST_NAME, FIRST_NAME ne sera pas modifié." + +#: views/users/api.hbs:27 views/users/api.hbs:42 views/users/api.hbs:50 +msgid "subscriber's email address" +msgstr "adresse mail de l'abonné" + +#: views/users/api.hbs:28 views/users/api.hbs:43 views/users/api.hbs:51 +#: views/users/api.hbs:59 views/users/api.hbs:82 views/users/api.hbs:90 +msgid "required" +msgstr "obligatoire" + +#: views/users/api.hbs:29 +msgid "subscriber's first name" +msgstr "prénom de l'abonné" + +#: views/users/api.hbs:30 +msgid "subscriber's last name" +msgstr "nom de l'abonné" + +#: views/users/api.hbs:31 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" +"Fuseau horaire de l'abonné (par ex. \"Europe/Paris\", \"CEST\" ou \"UTC\"). " +"Par défaut \"UTC\"" + +#: views/users/api.hbs:32 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" +"valeur du champ personnalisé. Utiliser yes/no pour des valeurs d'options " +"groupées (cases à cocher, choix sur listes, menus déroulants)" + +#: views/users/api.hbs:33 +msgid "Additional POST arguments" +msgstr "Arguments POST additionnels" + +#: views/users/api.hbs:34 +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 "" +"choisir \"oui\" si vous voulez être sur que l'adresse mail soit marquée " +"comme abonnée, même si elle était précédemment marquée désabonnée. Si " +"l'adresse mail était déjà désabonnée/bloquée, alors le statut d'abonnement " +"n'est pas changé" + +#: views/users/api.hbs:35 +msgid "" +"set to \"yes\" if you want to send confirmation email to the subscriber " +"before actually marking as subscribed" +msgstr "" +"choisir \"oui\" si vous voulez envoyer un mail de confirmation aux nouveaux " +"abonnés avant de marquer leur abonnement comme actif" + +#: views/users/api.hbs:37 +msgid "Remove subscription" +msgstr "Retirer l'abonnement" + +#: views/users/api.hbs:38 +msgid "This API call marks a subscription as unsubscribed" +msgstr "Ce service de l'API marque un abonnement comme désabonné" + +#: views/users/api.hbs:45 +msgid "Delete subscription" +msgstr "Supprimer l'abonnement" + +#: views/users/api.hbs:46 +msgid "This API call deletes a subscription" +msgstr "Ce service de l'API supprime un abonnement" + +#: views/users/api.hbs:53 +msgid "Add new custom field" +msgstr "Ajouter un champ personnalisé" + +#: views/users/api.hbs:54 +msgid "This API call creates a new custom field for a list." +msgstr "Ce service de l'API crée un nouveau champ personnalisé pour une liste." + +#: views/users/api.hbs:58 +msgid "field name" +msgstr "nom du champ" + +#: views/users/api.hbs:60 +msgid "one of the following types:" +msgstr "Un des types suivants :" + +#: views/users/api.hbs:61 +msgid "" +"If the type is 'option' then you also need to specify the parent element ID" +msgstr "" +"Si le type est 'option', vous devez également spécifier l'ID de l'élément " +"parent" + +#: views/users/api.hbs:62 +msgid "" +"Template for the group element. If not set, then values of the elements are " +"joined with commas" +msgstr "" +"Modèle pour le groupe d'éléments. Si vide, les valeurs seront concatennées " +"avec des virgules" + +#: views/users/api.hbs:63 +msgid "" +"if not visible then the subscriber can not view or modify this value at the " +"profile page" +msgstr "" +"Si masqué, l'abonné ne peut pas voir ni modifier la valeur depuis sa page de " +"profil" + +#: views/users/api.hbs:65 +msgid "Get list of blacklisted emails" +msgstr "Obtenir les adresses mail en liste noire" + +#: views/users/api.hbs:66 +msgid "This API call get list of blacklisted emails." +msgstr "Ce service de l'API renvoie la liste des mails dans la liste noire." + +#: views/users/api.hbs:71 +msgid "limit emails count in response" +msgstr "nombre maximal d'adresses mail retournées" + +#: views/users/api.hbs:73 +msgid "filter by part of email" +msgstr "Filtre sur partie d'adresse mail" + +#: views/users/api.hbs:74 +msgid "optional, default ''" +msgstr "optionnel, par défaut ''" + +#: views/users/api.hbs:77 +msgid "This API call either add emails to blacklist" +msgstr "Ce service de l'API ajoute des mails à la liste noire" + +#: views/users/api.hbs:81 views/users/api.hbs:89 +msgid "email address" +msgstr "adresse mail" + +#: views/users/api.hbs:84 +msgid "Delete email from blacklist" +msgstr "Supprimer le mail de la liste noire" + +#: views/users/api.hbs:85 +msgid "This API call either delete emails from blacklist" +msgstr "Ce service de l'API supprime des mails de la liste noire" + +#: views/users/api.hbs:92 +msgid "Get the lists a user has subscribed to" +msgstr "Obtenir la liste des listes auxquelles un utilisateur est abonné" + +#: views/users/api.hbs:93 +msgid "Retrieve the lists that the user with :email has subscribed to. " +msgstr "Récupérer les listes auxquelles l'utilisateur :email est abonné. " + +#: views/users/api.hbs:97 +msgid "Get all lists" +msgstr "Obtenir toutes les listes" + +#: views/users/api.hbs:98 +msgid "Retrieve every list. " +msgstr "Récupérer toutes les listes. " + +#: views/users/api.hbs:102 +msgid "Get list by id" +msgstr "Obtenir une liste via son id" + +#: views/users/api.hbs:103 +msgid "Retrieve the list with :id " +msgstr "Récupérer la liste avec :id " + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "Réinitialisation du mot de passe" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "Réinitialiser votre mot de passe ?" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "Les comptes sont gérés par le LDAP." + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "Réinitialiser le mot de passe" + +#: 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 "" +"Merci d'entrer le nom d'utilisateur et l'adresse mail que vous avez " +"fournis lors de votre inscription au service." + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "" +"Nous allons vous envoyer un mail qui vous permettra de réinitialiser votre " +"mot de passe." + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "Nom d'utilisateur ou adresse mail" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "Envoyer un mail de vérification" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "Se souvenir de moi" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "Mot de passe oublié ?" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "Choisissez votre nouveau mot de passe" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "Merci de saisir un nouveau mot de passe." + +#: lib/editor-helpers.js:17 routes/templates.js:95 +msgid "Could not find template with specified ID" +msgstr "Ne peut trouver de modèle avec cet identifiant" + +#: lib/editor-helpers.js:33 routes/archive.js:145 routes/campaigns.js:132 +#: routes/campaigns.js:285 routes/campaigns.js:380 routes/campaigns.js:428 +#: routes/campaigns.js:468 routes/campaigns.js:845 routes/campaigns.js:868 +#: routes/campaigns.js:887 routes/campaigns.js:909 routes/triggers.js:172 +msgid "Could not find campaign with specified ID" +msgstr "Aucune campagne ne correspond à cet identifiant" + +#: lib/editor-helpers.js:47 routes/editorapi.js:320 +msgid "Invalid resource type" +msgstr "Type de ressource invalide" + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "Code de statut d'erreur %s" + +#: lib/helpers.js:33 +msgid "URL that points to the unsubscribe page" +msgstr "URL vers la page de désabonnement" + +#: lib/helpers.js:36 +msgid "URL that points to the preferences page of the subscriber" +msgstr "URL vers la page de préférences de l'abonné" + +#: lib/helpers.js:39 +msgid "URL to preview the message in a browser" +msgstr "URL de visualisation du message dans le navigateur" + +#: lib/helpers.js:45 lib/models/segments.js:31 +msgid "First name" +msgstr "Prénom" + +#: lib/helpers.js:48 lib/models/segments.js:35 +msgid "Last name" +msgstr "Nom" + +#: lib/helpers.js:51 +msgid "Full name (first and last name combined)" +msgstr "Nom complet (Prénom et nom combinés)" + +#: lib/helpers.js:54 +msgid "Unique ID that identifies the recipient" +msgstr "Identifiant unique du destinataire" + +#: lib/helpers.js:57 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "Identifiant unique de la liste utilisée pour cette campagne" + +#: lib/helpers.js:60 +msgid "Unique ID that identifies current campaign" +msgstr "Identifiant unique de cette campagne" + +#: lib/helpers.js:68 lib/helpers.js:80 +msgid "content from an RSS entry" +msgstr "Contenu d'une entrée RSS" + +#: lib/helpers.js:71 +msgid "RSS entry title" +msgstr "Titre de l'entrée RSS" + +#: lib/helpers.js:74 +msgid "RSS entry date" +msgstr "Date de l'entrée RSS" + +#: lib/helpers.js:77 +msgid "RSS entry link" +msgstr "Lien de l'entrée RSS" + +#: lib/helpers.js:83 +msgid "RSS entry summary" +msgstr "Résumé de l'entrée RSS" + +#: lib/helpers.js:86 +msgid "RSS entry image URL" +msgstr "URL de l'image de l'entrée RSS" + +#: lib/mailer.js:245 +msgid "Invalid mail transport" +msgstr "Transport mail invalide" + +#: 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 "Identifiant de campagne manquant" + +#: lib/models/campaigns.js:241 +msgid "Emtpy or too large attahcment" +msgstr "Pièce jointe vide ou trop grande" + +#: lib/models/campaigns.js:408 lib/models/campaigns.js:600 +msgid "Campaign Name must be set" +msgstr "Le nom de la campagne est obligatoire" + +#: lib/models/campaigns.js:412 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "L'URL du flux RSS doit être renseignée et valide" + +#: lib/models/campaigns.js:568 +msgid "Selected template not found" +msgstr "Modèle inconnu" + +#: lib/models/campaigns.js:924 +msgid "Invalid or missing message ID" +msgstr "Message ID invalide ou manquant" + +#: lib/models/campaigns.js:1065 +msgid "Unrecognized message status" +msgstr "Message de statut non reconnu" + +#: lib/models/confirmations.js:27 +msgid "Could not store confirmation data" +msgstr "Les confirmation n'a pas pu être enregistrée" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "Menu déroulant" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "Date (MM/JJ/YYY)" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "Valeur JSON pour un affichage personnalisé" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "Option" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/forms.js:46 lib/models/lists.js:115 lib/models/lists.js:150 +#: lib/models/lists.js:265 lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:79 lib/models/subscriptions.js:390 +#: lib/models/subscriptions.js:566 lib/models/subscriptions.js:657 +#: lib/models/subscriptions.js:710 lib/models/subscriptions.js:773 +#: lib/models/subscriptions.js:816 +msgid "Missing List ID" +msgstr "Identifiant de liste manquant" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "Les champs Option nécessitent le choix d'un groupe" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "Identifiant de champ manquant" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "Le nom du champ est obligatoire" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "Champ personnalisé introuvable" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "Type de colonne \"%s\" inconnu" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "Nom de colonne manquant" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "Identifiant de liste manquant" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "Identifiant de la liste inconnu" + +#: 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 "Identifiant de fomulaire manquant" + +#: lib/models/forms.js:121 lib/models/forms.js:205 +msgid "Form Name must be set" +msgstr "Le nom du formulaire est obligatoire" + +#: lib/models/forms.js:307 +msgid "Custom form not found" +msgstr "Formulaire personnalisé introuvable" + +#: lib/models/links.js:337 routes/campaigns.js:534 routes/campaigns.js:582 +#: routes/campaigns.js:622 routes/campaigns.js:672 services/sender.js:305 +msgid "Campaign not found" +msgstr "Campagne introuvable" + +#: lib/models/links.js:345 routes/lists.js:181 services/sender.js:312 +msgid "List not found" +msgstr "Liste non trouvée" + +#: lib/models/links.js:353 +msgid "Subscription not found" +msgstr "Abonnement non trouvé" + +#: lib/models/lists.js:167 lib/models/lists.js:211 +msgid "List Name must be set" +msgstr "Le nom de la liste est obligatoire" + +#: lib/models/lists.js:294 +msgid "Missing List CID" +msgstr "Identifiant CID de liste manquant" + +#: lib/models/report-templates.js:26 lib/models/report-templates.js:70 +#: lib/models/report-templates.js:142 +msgid "Missing report template ID" +msgstr "Identifiant de modèle de rapport manquant" + +#: lib/models/report-templates.js:77 +msgid "Report template name must be set" +msgstr "Le nom du modèle de rapport est obligatoire" + +#: lib/models/reports.js:40 lib/models/reports.js:110 lib/models/reports.js:188 +msgid "Missing report ID" +msgstr "Identifiant de rapport manquant" + +#: lib/models/reports.js:116 +msgid "Report name must be set" +msgstr "Nom de rapport obligatoire" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "Pays d'inscription" + +#: lib/models/segments.js:19 lib/models/triggers.js:14 +msgid "Sign up date" +msgstr "Date d'inscription" + +#: lib/models/segments.js:23 lib/models/triggers.js:18 +msgid "Latest open" +msgstr "Dernière ouverture" + +#: lib/models/segments.js:27 lib/models/triggers.js:22 +msgid "Latest click" +msgstr "Dernier 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 "Identifiant de segment manquant" + +#: lib/models/segments.js:85 lib/models/segments.js:551 +#: lib/models/segments.js:661 +msgid "Segment not found" +msgstr "Segment introuvable" + +#: lib/models/segments.js:146 lib/models/segments.js:147 +#: lib/models/segments.js:409 lib/models/segments.js:410 +msgid "%s days after today" +msgstr "%s jours à partir d'aujourd'hui" + +#: lib/models/segments.js:146 lib/models/segments.js:147 +#: lib/models/segments.js:409 lib/models/segments.js:410 +msgid "%s days before today" +msgstr "%s days avant aujourd'hui" + +#: lib/models/segments.js:148 lib/models/segments.js:411 +msgid "today" +msgstr "aujourd'hui" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "Règle de segmentation invalide" + +#: lib/models/segments.js:289 lib/models/segments.js:455 routes/segments.js:266 +#: routes/segments.js:300 routes/segments.js:370 routes/segments.js:381 +msgid "Selected segment not found" +msgstr "Segment inconnu" + +#: lib/models/segments.js:294 lib/models/segments.js:460 routes/segments.js:272 +#: routes/segments.js:306 routes/segments.js:387 +msgid "Invalid rule type" +msgstr "Type de règle invalide" + +#: lib/models/segments.js:359 lib/models/segments.js:435 +#: lib/models/segments.js:526 +msgid "Missing Rule ID" +msgstr "Identifiant de règle manquant" + +#: lib/models/segments.js:375 +msgid "Specified rule not found" +msgstr "La ègle demandée est introuvable" + +#: lib/models/segments.js:386 +msgid "Specified segment not found" +msgstr "Le segment spécifié est introuvable" + +#: lib/models/segments.js:446 +msgid "Selected rule not found" +msgstr "Règle inconnue" + +#: lib/models/subscriptions.js:257 lib/models/subscriptions.js:287 +#: lib/models/subscriptions.js:394 +msgid "Missing Subscription ID" +msgstr "Identifiant d'abonnement manquant" + +#: lib/models/subscriptions.js:315 +msgid "Missing Subscription email address" +msgstr "Adresse mail d'abonnement manquante" + +#: lib/models/subscriptions.js:570 lib/models/subscriptions.js:820 +msgid "Missing subscription ID" +msgstr "Identifiant d'abonnement manquant" + +#: lib/models/subscriptions.js:661 lib/models/subscriptions.js:714 +#: lib/models/subscriptions.js:750 +msgid "Missing Import ID" +msgstr "Identifiant d'import manquant" + +#: lib/models/subscriptions.js:842 +msgid "Unknown subscription ID" +msgstr "ID d'abonnement inconnu" + +#: lib/models/subscriptions.js:847 routes/subscription.js:639 +msgid "Nothing seems to be changed" +msgstr "Rien ne semble avoir été modifié" + +#: lib/models/subscriptions.js:913 routes/subscription.js:473 +#: routes/subscription.js:545 routes/subscription.js:581 +#: routes/subscription.js:697 routes/subscription.js:772 +msgid "Subscription not found in this list" +msgstr "Abonnement introuvable dans cette liste" + +#: lib/models/templates.js:26 lib/models/templates.js:102 +#: lib/models/templates.js:157 +msgid "Missing Template ID" +msgstr "Identifiant de modèle manquant" + +#: lib/models/templates.js:55 lib/models/templates.js:106 +msgid "Template Name must be set" +msgstr "Le nom du modèle est obligatoire" + +#: lib/models/templates.js:147 +msgid "Template does not exist" +msgstr "Le modèle n'existe pas" + +#: lib/models/triggers.js:31 +msgid "Has Opened" +msgstr "A ouvert" + +#: lib/models/triggers.js:34 +msgid "Has Clicked" +msgstr "A cliqué" + +#: lib/models/triggers.js:37 +msgid "Not Opened" +msgstr "Non ouvert" + +#: lib/models/triggers.js:40 +msgid "Not Clicked" +msgstr "Non cliqué" + +#: lib/models/triggers.js:221 lib/models/triggers.js:258 +msgid "Missing or invalid list ID" +msgstr "Identifiant de liste manquant ou invalide" + +#: lib/models/triggers.js:225 lib/models/triggers.js:310 +msgid "Days in the past are not allowed" +msgstr "Jours dans le passé interdits" + +#: lib/models/triggers.js:229 lib/models/triggers.js:250 +#: lib/models/triggers.js:314 lib/models/triggers.js:335 +msgid "Missing or invalid trigger rule" +msgstr "Règle de déclenchement manquante ou invalide" + +#: lib/models/triggers.js:236 lib/models/triggers.js:321 +msgid "Invalid subscription configuration" +msgstr "Configuration d'abonnement invalide" + +#: lib/models/triggers.js:243 lib/models/triggers.js:328 +msgid "Invalid campaign configuration" +msgstr "Configuration incorrecte de la campagne" + +#: lib/models/triggers.js:246 lib/models/triggers.js:331 +msgid "A campaing can not be a target for itself" +msgstr "Une campagne ne peut pas être envoyée à elle-même" + +#: lib/models/triggers.js:279 +msgid "Could not store trigger row" +msgstr "Le déclencheur n'a pas pu être enregistré" + +#: lib/models/triggers.js:296 +msgid "Missing or invalid Trigger ID" +msgstr "Identifiant de déclencheur manquant ou invalide" + +#: lib/models/triggers.js:363 +msgid "Missing Trigger ID" +msgstr "Identifiant de déclencheur manquant" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "L'utilisateur n'a pas pu être enregistré" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "L'adresse mail est obligatoire" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "Échec à la vérification des données utilisateur" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" +"Ne peut changer l'adresse mail, car un autre utilisateur avec la même " +"adresse existe déjà" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "Mot de passe actuel incorrect" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "Nouveau mot de passe non enregistré" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "Les mots de passe ne correspondent pas" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "Identifiant utilisateur non fourni" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "Nom d'utilisateur obligatoire" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "Demande de changement de mot de passe" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "Token de réinitialisation ou nom d'utilisateur manquant" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "Nouveau mot de passe invalide" + +#: lib/passport.js:50 +msgid "%s logged out" +msgstr "%s déconnecté" + +#: lib/passport.js:65 +msgid "Failed to authenticate user" +msgstr "Échec à l'authentification de l'utilisateur" + +#: lib/passport.js:81 +msgid "Logged in as %s" +msgstr "Connecté comme %s" + +#: lib/passport.js:191 +msgid "Incorrect username or password" +msgstr "Nom d'utilisateur ou mot de passe incorrect" + +#: lib/subscription-mail-helpers.js:28 +msgid "%s: Subscription Confirmed" +msgstr "%s: Abonnement confirmé" + +#: lib/subscription-mail-helpers.js:39 +msgid "%s: Email Address Already Registered" +msgstr "%s: Adresse mail déjà enregistrée" + +#: lib/subscription-mail-helpers.js:49 +msgid "%s: Please Confirm Email Change in Subscription" +msgstr "%s: Merci de confirmer le changement de mail de l'abonnement" + +#: lib/subscription-mail-helpers.js:59 +msgid "%s: Please Confirm Subscription" +msgstr "%: Merci de confirmer l'abonnement" + +#: lib/subscription-mail-helpers.js:69 +msgid "%s: Please Confirm Unsubscription" +msgstr "%: Merci de confirmer le désabonnement" + +#: lib/subscription-mail-helpers.js:76 +msgid "%s: Unsubscription Confirmed" +msgstr "%s: Désabonnement confirmé" + +#: lib/tools.js:154 +msgid "Blocked email address \"%s\"" +msgstr "Adresse mail bloquée \"%s\"" + +#: lib/tools.js:163 +msgid "Invalid email address \"%s\"." +msgstr "Adresse mail \"%s\" invalide." + +#: lib/tools.js:166 +msgid "MX record not found for domain" +msgstr "Enregistrement MX introuvable pour le domaine" + +#: lib/tools.js:169 +msgid "Address domain not found" +msgstr "Le nom de domaine de l'adresse est inconnu" + +#: lib/tools.js:172 +msgid "Address domain name is required" +msgstr "Le nom de domaine de l'adresse est requis" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:230 +msgid "Not Found" +msgstr "Non trouvé" + +#: routes/archive.js:121 services/sender.js:454 +msgid "Received status code %s from %s" +msgstr "Le code de statut %s a été reçu de %s" + +#: routes/archive.js:153 routes/campaigns.js:895 +msgid "Attachment not found" +msgstr "Pièce jointe introuvable" + +#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:32 +#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:15 +#: 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:19 routes/users.js:75 +#: routes/users.js:120 +msgid "Need to be logged in to access restricted content" +msgstr "Vous devez être connecté pour accéder à cet espace" + +#: routes/campaigns.js:118 +msgid "Could not create campaign" +msgstr "La campagne n'a pas pu être créée" + +#: routes/campaigns.js:121 +msgid "Campaign “%s” created" +msgstr "Campagne \"%s\" créée" + +#: routes/campaigns.js:210 +msgid "Campaign settings updated" +msgstr "Les paramètres de la campagne ont été mis à jour" + +#: routes/campaigns.js:212 +msgid "Campaign settings not updated" +msgstr "Les paramètres de la campagne n'ont pas pu être mis à jour" + +#: routes/campaigns.js:228 routes/campaigns.js:745 +msgid "Campaign deleted" +msgstr "Campagne supprimée" + +#: routes/campaigns.js:230 routes/campaigns.js:747 +msgid "Could not delete specified campaign" +msgstr "La campagne n'a pas pu être supprimée" + +#: routes/campaigns.js:249 +msgid "Idling" +msgstr "Inactif" + +#: routes/campaigns.js:252 +msgid "Scheduled" +msgstr "Programmé" + +#: routes/campaigns.js:258 +msgid "Paused" +msgstr "En pause" + +#: routes/campaigns.js:260 +msgid "Inactive" +msgstr "Inactif" + +#: routes/campaigns.js:262 +msgid "Active" +msgstr "Actif" + +#: routes/campaigns.js:264 +msgid "Other" +msgstr "Autre" + +#: routes/campaigns.js:422 +msgid "Unknown status selector" +msgstr "Sélecteur de statut inconnu" + +#: routes/campaigns.js:763 +msgid "Scheduled sending" +msgstr "Envoi programmé" + +#: routes/campaigns.js:765 +msgid "Could not schedule sending" +msgstr "L'envoi n'a pas pu être programmé" + +#: routes/campaigns.js:777 +msgid "Sending resumed" +msgstr "Envoi relancé" + +#: routes/campaigns.js:779 +msgid "Could not resume sending" +msgstr "L'envoi n'a pas pu être relancé" + +#: routes/campaigns.js:791 +msgid "Sending reset" +msgstr "Envoi réinitialisé" + +#: routes/campaigns.js:793 +msgid "Could not reset sending" +msgstr "L'envoi n'a pas pu être réinitialisé" + +#: routes/campaigns.js:805 routes/campaigns.js:833 +msgid "Sending paused" +msgstr "Envoi mis en pause" + +#: routes/campaigns.js:807 routes/campaigns.js:835 +msgid "Could not pause sending" +msgstr "L'envoi n'a pas pu être interrompu" + +#: routes/campaigns.js:819 +msgid "Sending activated" +msgstr "Envoi activé" + +#: routes/campaigns.js:821 +msgid "Could not activate sending" +msgstr "Impossible d'activer l'envoi" + +#: routes/campaigns.js:856 +msgid "Attachment uploaded" +msgstr "Pièce jointe téléchargée" + +#: routes/campaigns.js:858 +msgid "Could not store attachment" +msgstr "La pièce jointe n'a pas pu être enregistrée" + +#: routes/campaigns.js:875 +msgid "Attachment deleted" +msgstr "Pièce jointe supprimée" + +#: routes/campaigns.js:877 +msgid "Could not delete attachment" +msgstr "La pièce jointe n'a pas pu être supprimée" + +#: routes/editorapi.js:38 +msgid "Invalid editor name" +msgstr "Nom d'éditeur invalide" + +#: routes/editorapi.js:146 +msgid "Method not supported" +msgstr "Méthode non supportée" + +#: routes/editorapi.js:381 +msgid "Invalid resource type or ID" +msgstr "Type ou ID de ressource invalide" + +#: 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 "Identifiant de liste inconnu" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "Le champ personnalisé n'a pas pu être créé" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "Champ inconnu" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "Les paramètres du champ ont été enregistrés" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "Les paramètres du champ n'ont pas pu être mis à jour" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "Champ personnalisé supprimé" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "Le champ n'a pas pu être supprimé" + +#: routes/forms.js:78 +msgid "Could not create custom form" +msgstr "Le formulaire n'a pas pu être créé" + +#: routes/forms.js:105 +msgid "Selected form not found" +msgstr "Formulaire inconnu" + +#: routes/forms.js:136 +msgid "The plaintext version for this email" +msgstr "Version texte de ce mail" + +#: routes/forms.js:137 +msgid "Custom forms use MJML for formatting" +msgstr "Les formulaires personnalisés utilisent le formatage MJML" + +#: routes/forms.js:138 +msgid "See the MJML documentation here" +msgstr "Voir ici la documentation MJML" + +#: routes/forms.js:146 +msgid "Layout" +msgstr "Format" + +#: routes/forms.js:152 +msgid "Form Input Style" +msgstr "Style des zones Input de formulaire" + +#: routes/forms.js:154 +msgid "" +"This CSS stylesheet defines the appearance of form input elements and alerts" +msgstr "" +"Cette feuille de style CSS définit l'apparence des zones de saisie et des " +"alertes du formulaire" + +#: routes/forms.js:160 +msgid "Web - Subscribe" +msgstr "Web - Abonnement" + +#: routes/forms.js:165 +msgid "Web - Confirm Subscription Notice" +msgstr "Web - Information de confirmation d'abonnement" + +#: routes/forms.js:170 +msgid "Mail - Confirm Subscription (MJML)" +msgstr "Mail - Confirmation d'abonnement (MJML)" + +#: routes/forms.js:175 +msgid "Mail - Confirm Subscription (Text)" +msgstr "Mail - Confirmation d'abonnement (Texte)" + +#: routes/forms.js:180 +msgid "Mail - Already Subscribed (MJML)" +msgstr "Mail - Déjà abonné (MJML)" + +#: routes/forms.js:185 +msgid "Mail - Already Subscribed (Text)" +msgstr "Mail - Déjà abonné (Texte)" + +#: routes/forms.js:190 +msgid "Web - Subscribed Notice" +msgstr "Web - Information d'abonné" + +#: routes/forms.js:195 +msgid "Mail - Subscription Confirmed (MJML)" +msgstr "Mail - Abonnement confirmé (MJML)" + +#: routes/forms.js:200 +msgid "Mail - Subscription Confirmed (Text)" +msgstr "Mail - Abonnement confirmé (Texte)" + +#: routes/forms.js:208 +msgid "Web - Manage Preferences" +msgstr "Web - Gestion des préférences" + +#: routes/forms.js:213 +msgid "Web - Manage Address" +msgstr "Web - Gestion de l'adresse" + +#: routes/forms.js:218 +msgid "Web - Updated Notice" +msgstr "Web - Information enregistrée" + +#: routes/forms.js:226 +msgid "Web - Unsubscribe" +msgstr "Web - Désabonnement" + +#: routes/forms.js:231 +msgid "Web - Confirm Unsubscription Notice" +msgstr "Web - Information de confirmation de désabonnement" + +#: routes/forms.js:236 +msgid "Mail - Confirm Unsubscription (MJML)" +msgstr "Mail - Confirmation de désabonnement (MJML)" + +#: routes/forms.js:241 +msgid "Mail - Confirm Unsubscription (Text)" +msgstr "Mail - Confirmation de désabonnement (Texte)" + +#: routes/forms.js:246 +msgid "Mail - Confirm Address Change (MJML)" +msgstr "Mail - Confirmation de changement d'adresse (MJML)" + +#: routes/forms.js:251 +msgid "Mail - Confirm Address Change (Text)" +msgstr "Mail - Confirmation de changement d'adresse (Texte)" + +#: routes/forms.js:256 +msgid "Web - Unsubscribed Notice" +msgstr "Web - Information de confirmation de désabonnement" + +#: routes/forms.js:261 +msgid "Mail - Unsubscription Confirmed (MJML)" +msgstr "Mail - Désbonnement confirmé (MJML)" + +#: routes/forms.js:266 +msgid "Mail - Unsubscription Confirmed (Text)" +msgstr "Mail - Désbonnement confirmé (Texte)" + +#: routes/forms.js:271 +msgid "Web - Manual Unsubscribe Notice" +msgstr "Web - Information de désabonnement manuel" + +#: routes/forms.js:309 +msgid "Form settings updated" +msgstr "Les paramètres du formulaire ont été enregistrés" + +#: routes/forms.js:311 +msgid "Form settings not updated" +msgstr "Les paramètres du formulaire n'ont pas pu être modifiés" + +#: routes/forms.js:327 +msgid "Custom form deleted" +msgstr "Formulaire personnalisé supprimé" + +#: routes/forms.js:329 +msgid "Could not delete specified form" +msgstr "Le formulaire n'a pas pu être supprimé" + +#: routes/index.js:11 +msgid "Self Hosted Newsletter App" +msgstr "Solution de Newsletter auto-hébergée" + +#: routes/links.js:39 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "Oups, nous n'avons pas trouvé le lien sur lequel vous avez cliqué" + +#: routes/lists.js:82 +msgid "Could not create list" +msgstr "La liste n'a pas pu être créée" + +#: routes/lists.js:85 +msgid "List created" +msgstr "Liste créée" + +#: 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:657 routes/lists.js:714 routes/lists.js:741 +msgid "Could not find list with specified ID" +msgstr "Ne peut trouver la liste avec cet identifiant" + +#: routes/lists.js:122 +msgid "List settings updated" +msgstr "Les paramètres de la liste ont été mis à jour" + +#: routes/lists.js:124 +msgid "List settings not updated" +msgstr "Les paramètres de la liste n'ont pu être enregistrés" + +#: routes/lists.js:142 +msgid "List deleted" +msgstr "Liste supprimée" + +#: routes/lists.js:144 +msgid "Could not delete specified list" +msgstr "La liste n'a pas pu être supprimée" + +#: routes/lists.js:206 +msgid "Unknown" +msgstr "Inconnu" + +#: routes/lists.js:206 +msgid "Complained" +msgstr "Plainte" + +#: routes/lists.js:237 +msgid "Invalid key" +msgstr "Clé invalide" + +#: routes/lists.js:239 +msgid "Expired key" +msgstr "Clé expirée" + +#: routes/lists.js:241 +msgid "Revoked key" +msgstr "Clé révoquée" + +#: routes/lists.js:288 +msgid "Force Subscribe" +msgstr "Forcer l'abonnement" + +#: routes/lists.js:291 +msgid "Initializing" +msgstr "Initialisation" + +#: routes/lists.js:294 +msgid "Initialized" +msgstr "Initialisé" + +#: routes/lists.js:297 +msgid "Importing" +msgstr "Import en cours" + +#: routes/lists.js:303 +msgid "Errored" +msgstr "En erreur" + +#: routes/lists.js:381 routes/lists.js:450 routes/lists.js:475 +msgid "Could not find subscriber with specified ID" +msgstr "Ne peut trouver d'abonné avec cet identifiant" + +#: routes/lists.js:427 +msgid "Could not add subscription" +msgstr "L'abonnement n'a pas pu être ajouté" + +#: routes/lists.js:432 +msgid "%s was successfully added to your list" +msgstr "%s a été ajouté à votre liste" + +#: routes/lists.js:434 +msgid "%s was not added to your list" +msgstr "%s n'a pas été ajouté à votre liste" + +#: routes/lists.js:456 +msgid "Could not unsubscribe user" +msgstr "L'utilisateur n'a pas pu être désabonné" + +#: routes/lists.js:459 +msgid "%s was successfully unsubscribed from your list" +msgstr "%s a été désabonné de votre liste" + +#: routes/lists.js:479 +msgid "%s was successfully removed from your list" +msgstr "%s a été retiré de votre liste" + +#: routes/lists.js:491 +msgid "Another subscriber with email address %s already exists" +msgstr "Un autre abonné avec l'adresse mail %s existe déjà" + +#: routes/lists.js:498 +msgid "Subscription settings updated" +msgstr "Les paramètres de l'abonnement ont été enregitrés" + +#: routes/lists.js:500 +msgid "Subscription settings not updated" +msgstr "Les paramètres de l'abonnement n'ont pas pu être mis à jour" + +#: routes/lists.js:542 routes/lists.js:663 routes/lists.js:699 +#: routes/lists.js:727 routes/lists.js:747 +msgid "Could not find import data with specified ID" +msgstr "Ne peut trouver l'import de données avec cet identifiant" + +#: routes/lists.js:573 +msgid "Could not process CSV" +msgstr "Le CSV n'a pas pu être traité" + +#: routes/lists.js:589 +msgid "Could not create importer" +msgstr "L'import n'a pas pu être créé" + +#: routes/lists.js:643 +msgid "Empty file" +msgstr "Fichier vide" + +#: routes/lists.js:646 +msgid "Too few rows" +msgstr "Trop peu de lignes" + +#: routes/lists.js:703 +msgid "Import started" +msgstr "Import démarré" + +#: routes/lists.js:731 +msgid "Import restarted" +msgstr "Import relancé" + +#: routes/lists.js:797 +msgid "One-step (i.e. no email with confirmation link)" +msgstr "En une étape (pas d'envoi de mail avec lien de confirmation)" + +#: routes/lists.js:803 +msgid "" +"One-step with unsubscription form (i.e. no email with confirmation link)" +msgstr "" +"En une étape avec formulaire de désabonnement (pas d'envoi de mail avec lien " +"de confirmation)" + +#: routes/lists.js:809 +msgid "Two-step (i.e. an email with confirmation link will be sent)" +msgstr "En deux étapes (envoi d'un mail avec avec lien de confirmation)" + +#: routes/lists.js:815 +msgid "" +"Two-step with unsubscription form (i.e. an email with confirmation link will " +"be sent)" +msgstr "" +"En deux étapes avec formulaire de désabonnement (envoi d'un mail avec avec " +"lien de confirmation)" + +#: routes/lists.js:821 +msgid "" +"Manual (i.e. unsubscription has to be performed by the list administrator)" +msgstr "" +"Manuel (le désabonnement doit être fait par l'administrateur de la liste)" + +#: routes/report-templates.js:246 +msgid "Could not create report template" +msgstr "Le modèle de rapport n'a pas pu être créé" + +#: routes/report-templates.js:249 +msgid "Report template “%s” created" +msgstr "Modèle de rapport \"%s\" créé" + +#: routes/report-templates.js:257 +msgid "Could not find report template with specified ID" +msgstr "Ne peut trouver le modèle de rapport avec cet identifiant" + +#: routes/report-templates.js:280 +msgid "Report template updated" +msgstr "Modèle de rapport enregistré" + +#: routes/report-templates.js:282 +msgid "Report template not updated" +msgstr "Le modèle de rapport n'a pas pu être enregistré" + +#: routes/report-templates.js:298 +msgid "Report template deleted" +msgstr "Modèle de rapport supprimé" + +#: routes/report-templates.js:300 +msgid "Could not delete specified report template" +msgstr "Le modèle de rapport n'a pas pu être supprimé" + +#: routes/reports.js:124 routes/reports.js:130 +msgid "Could not create report" +msgstr "Le rapport n'a pas pu être créé" + +#: routes/reports.js:135 +msgid "Report “%s” created" +msgstr "Rapport \"%s\" créé" + +#: 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 "Ne peut trouver le rapport avec cet identifiant" + +#: routes/reports.js:188 routes/reports.js:194 +msgid "Could not update report" +msgstr "Le rapport n'a pas pu être mis à jour" + +#: routes/reports.js:197 +msgid "Report updated" +msgstr "Rapport enregistré" + +#: routes/reports.js:199 +msgid "Report not updated" +msgstr "Le rapport n'a pas pu être enregistré" + +#: routes/reports.js:212 +msgid "Report deleted" +msgstr "Rapport supprimé" + +#: routes/reports.js:214 +msgid "Could not delete specified report" +msgstr "Le rapport n'a pas pu être supprimé" + +#: routes/reports.js:230 +msgid "Could not find report template" +msgstr "Le modèle de rapport associé n'a pas pu être trouvé" + +#: routes/reports.js:260 +msgid "Unknown type of template" +msgstr "Type de modèle inconnu" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "Le segment n'a pas pu être créé" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "Segment créé" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "Identifiant de segment inconnu" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "Configuration du segment enregistrée" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "Configuration du segment non enregistrée" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "Segment supprimé" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "Le segment n'a pas pu être supprimé" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "La règle n'a pas pu être créée" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "Règle créée" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "Paramètres de la règle enregistrés" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "Paramètres de la règle non mis à jour" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "Règle supprimée" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "La règle n'a pas pu être supprimée" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "Utiliser TLS" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "Utilisé habituellement pour le port 465" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "Utiliser STARTTLS" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "Utilisé habituellement pour les ports 587 et 25" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "Ne pas utiliser le chiffrement" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "Configuration mise à jour" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "Type de transport mail invalide" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "Clé d'accès invalide" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "Credentials AWS invalides" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "Connexion refusée, vérifiez l'adresse du serveur et le port." + +#: 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 "" +"N'a pas reçu le message de présentation du serveur. Cela peut survenir lors " +"d'une connexion à port TLS sans utiliser TLS" + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "N'a pas reçu le message de présentation du serveur." + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" +"Connexion expirée. Vérifiez vos règles de firewall, le port de destination " +"est certainement bloqué." + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "" +"La tentative d'authentification est rejetée. Le serveur impose l'utilisation " +"de STARTTLS." + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "" +"Échec de l'authentification, vérifiez le nom d'utilisateur et le mot de " +"passe." + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "Echec à la vérification du serveur mail." + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "Le serveur a répondu : \"%s\"" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "Paramètres mailer vérifiés, prêt à envoyer du mail !" + +#: routes/subscription.js:33 +msgid "Not allowed by CORS" +msgstr "Interdit par CORS" + +#: routes/subscription.js:61 routes/subscription.js:176 +#: routes/subscription.js:286 routes/subscription.js:382 +#: routes/subscription.js:459 routes/subscription.js:535 +#: routes/subscription.js:566 routes/subscription.js:626 +#: routes/subscription.js:682 routes/subscription.js:760 +#: routes/subscription.js:897 +msgid "Selected list not found" +msgstr "Liste inconnue" + +#: routes/subscription.js:93 +msgid "Could not save subscription" +msgstr "L'abonnement n'a pas pu être enregistré" + +#: routes/subscription.js:118 +msgid "Subscriber info corrupted or missing" +msgstr "Information sur l'abonné manquante ou corrompue" + +#: routes/subscription.js:136 +msgid "Email address changed" +msgstr "Adresse mail changée" + +#: routes/subscription.js:179 routes/subscription.js:385 +msgid "The list does not allow public subscriptions." +msgstr "La liste ne permet pas les abonnements libres." + +#: routes/subscription.js:355 routes/subscription.js:357 +msgid "Email address not set" +msgstr "L'adresse mail n'est pas précisée" + +#: routes/subscription.js:653 +msgid "" +"An email with further instructions has been sent to the provided address" +msgstr "Un mail avec des instructions a été envoyé à l'adresse fournie" + +#: routes/subscription.js:861 routes/subscription.js:877 +msgid "Public key is not set" +msgstr "La clé publique n'est pas renseignée" + +#: routes/templates.js:84 +msgid "Could not create template" +msgstr "Le modèle n'a pas pu être créé" + +#: routes/templates.js:87 +msgid "Template created" +msgstr "Modèle créé" + +#: routes/templates.js:126 +msgid "Template settings updated" +msgstr "Paramètres du modèle enregistrés" + +#: routes/templates.js:128 +msgid "Template settings not updated" +msgstr "Les paramètres du modèle n'ont pas pu être mis à jour" + +#: routes/templates.js:144 +msgid "Template duplicated" +msgstr "Modèle dupliqué" + +#: routes/templates.js:146 +msgid "Could not duplicate specified template" +msgstr "Le modèle n'a pas pu être dupliqué" + +#: routes/templates.js:157 +msgid "Template deleted" +msgstr "Modèle supprimé" + +#: routes/templates.js:159 +msgid "Could not delete specified template" +msgstr "Le modèle n'a pas pu être supprimé" + +#: routes/triggers.js:64 routes/triggers.js:88 routes/triggers.js:180 +msgid "Could not find selected list" +msgstr "La liste sélectionnée n'a pas pu être trouvée" + +#: routes/triggers.js:94 routes/triggers.js:100 routes/triggers.js:186 +msgid "Error while finding selected segment" +msgstr "Erreur à la recherche du segment sélectionné" + +#: routes/triggers.js:157 +msgid "Could not create trigger" +msgstr "Le déclencheur n'a pas pu être créé" + +#: routes/triggers.js:164 +msgid "Trigger “%s” created" +msgstr "Déclencheur \"%s\" créé" + +#: routes/triggers.js:197 +msgid "Error while finding selected segment subscribers" +msgstr "Erreur lors de la recherche des abonnés du segment" + +#: routes/triggers.js:262 +msgid "Trigger settings updated" +msgstr "Paramètres du déclencheur enregistrés" + +#: routes/triggers.js:264 +msgid "Trigger settings not updated" +msgstr "Les paramètres du déclencheur n'ont pas pu être enregistrés" + +#: routes/triggers.js:276 +msgid "Trigger deleted" +msgstr "Déclencheur supprimé" + +#: routes/triggers.js:278 +msgid "Could not delete specified trigger" +msgstr "Le déclencheur n'a pas pu être supprimé" + +#: routes/triggers.js:290 +msgid "Could not find trigger with specified ID" +msgstr "Ne peut trouver de déclencheur avec cet identifiant" + +#: routes/triggers.js:303 +msgid "Trigger not found" +msgstr "Déclencheur inconnu" + +#: 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 mail avec les instructions de réinitialisation du mot de passe à été " +"envoyée à votre adresse, si elle est enregistrée dans notre système." + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "Token de réinitialisation inconnu ou expiré" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "Votre mot de passe a bien été changé" + +#: routes/users.js:87 +msgid "User data not found" +msgstr "Données utilisateurs non trouvées" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "Le jeton d'accès a été mis à jour" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "Le jeton d'accès n'a pas été mis à jour" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "Les informations sur le compte ont été mises à jour" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "Les informations sur le compte n'ont pas été mises à jour" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "Erreur du flux : %s" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "%s nouveau(x) message(s) ont été trouvés dans le flux RSS" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "Aucun nouvel élément dans le flux RSS" + +#: services/feedcheck.js:146 +msgid "RSS entry %s" +msgstr "Entrée RSS %s" + +#: services/importer.js:249 +msgid "Could not access import file" +msgstr "Ne peut ouvrir le fichier d'import" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "Type de déclencheur %s inconnu" diff --git a/languages/hu_HU.mo b/languages/hu_HU.mo new file mode 100644 index 00000000..524af8aa Binary files /dev/null and b/languages/hu_HU.mo differ diff --git a/languages/hu_HU.po b/languages/hu_HU.po new file mode 100644 index 00000000..8d42cfe9 --- /dev/null +++ b/languages/hu_HU.po @@ -0,0 +1,4521 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Language-Team: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2018-09-10 16:02+0200\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2018-07-29 01:47+0200\n" +"X-Generator: Poedit 1.8.7.1\n" +"Last-Translator: \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Language: hu_HU\n" + +#: views/archive/layout.hbs:1 views/layout.hbs:1 +msgid "Self hosted email newsletter app" +msgstr "Saját üzemeltetésű hírlevél-program" + +#: 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 "Nyitólap" + +#: views/blacklist.hbs:2 views/blacklist.hbs:3 views/layout.hbs:7 +#: views/lists/subscription/edit.hbs:15 +msgid "Blacklist" +msgstr "Feketelista" + +#: views/blacklist.hbs:4 views/users/api.hbs:76 +msgid "Add email to blacklist" +msgstr "Email feketelistázása" + +#: views/blacklist.hbs:5 +msgid "Add" +msgstr "Hozzáadás" + +#: views/blacklist.hbs:6 +msgid "Email" +msgstr "Email" + +#: 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 "Kampányok" + +#: views/campaigns/blacklisted.hbs:3 views/campaigns/blacklisted.hbs:4 +msgid "Blacklisted info" +msgstr "Feketelista info" + +#: 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 "Kampány megtekintése" + +#: views/campaigns/blacklisted.hbs:6 +msgid "Subscribers who blacklisted by global blacklist:" +msgstr "Feliratkozók, akik szerepelnek a globális feketelistán" + +#: 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 "Cím" + +#: 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 "Keresztnév" + +#: 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 "Vezetéknév" + +#: views/campaigns/blacklisted.hbs:10 +msgid "Reason" +msgstr "Ok" + +#: views/campaigns/blacklisted.hbs:11 +msgid "Time" +msgstr "Időpont" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Visszapattanás info" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Feliratkozók, akik visszapattanás miatt le lettek íratva:" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "SMTP válasz" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Visszapattanás ideje" + +#: 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 "Kampány létrehozása" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Sima kampány" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "RSS kampány" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "" + +#: 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 "Név" + +#: 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 "Leírás" + +#: 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 "Státusz" + +#: 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 "Létrehozva" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Link információ" + +#: 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 "Kattintások" + +#: views/campaigns/clicked.hbs:8 views/campaigns/view.hbs:65 +msgid "% of clicks" +msgstr "%-a a kattintásoknak" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:66 +msgid "% of messages" +msgstr "%-a az üzeneteknek" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:69 +msgid "Aggregated clicks" +msgstr "Összesített kattintások" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Feliratkozók, akik rákattintottak egy linkre:" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Feliratkozók, akik rákattintottak erre a linkre:" + +#: views/campaigns/clicked.hbs:13 views/campaigns/opened.hbs:7 +msgid "Stats by country" +msgstr "Országok szerinti statisztika" + +#: views/campaigns/clicked.hbs:14 views/campaigns/opened.hbs:8 +msgid "Stats by device type" +msgstr "Eszköz szerinti statisztika" + +#: views/campaigns/clicked.hbs:18 +msgid "First click time" +msgstr "" + +#: views/campaigns/clicked.hbs:19 +msgid "Click count" +msgstr "Kattintások száma" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Reklamálások info" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Felhasználók, akik reklamáltak, ezért le lettek íratva" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Reklamálás ideje" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:21 +msgid "Create RSS Campaign" +msgstr "RSS kampány létrehozása" + +#: 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 "" + +#: 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 "Kampány neve" + +#: 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 "HTML megengedett" + +#: 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 "Kiválasztás" + +#: 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 "feliratkozók" + +#: views/campaigns/create-rss.hbs:13 views/campaigns/edit-rss.hbs:15 +msgid "RSS Feed Url" +msgstr "RSS URL-je" + +#: 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 "" + +#: 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 feladó neve" + +#: 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 "Ez az a név, ami alatt az emailek meg fognak jelenni" + +#: 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 "Email feladó címe" + +#: 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 "Ez a cím, amiről érkezni látják majd a leveleket" + +#: 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 "Megnyitások követésének letiltása" + +#: 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 "Kattintások követésének letiltása" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "" + +#: 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 "Sablon" + +#: views/campaigns/create-triggered.hbs:13 views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "Sablon kiválasztása:" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "" + +#: 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 "" + +#: 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 tárgya" + +#: 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 "" + +#: 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 "" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "RSS kampány szerkesztése" + +#: 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 "Általános beállítások" + +#: 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 "" + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:35 +msgid "Delete Campaign" +msgstr "Kampány törlése" + +#: views/campaigns/edit-rss.hbs:25 views/campaigns/edit-triggered.hbs:28 +#: views/campaigns/edit.hbs:36 views/lists/edit.hbs:22 +#: 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:97 views/templates/edit.hbs:13 +#: views/triggers/edit.hbs:30 views/users/account.hbs:18 +msgid "Update" +msgstr "Frissítés" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +#: routes/forms.js:143 +msgid "General" +msgstr "" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:27 +msgid "Template Settings" +msgstr "Sablon beállítások" + +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:28 +msgid "Template URL" +msgstr "Sablon URL" + +#: 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 "Kampány szerkesztése" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:30 +msgid "Attachments" +msgstr "Csatolmányok" + +#: views/campaigns/edit.hbs:31 +msgid "File" +msgstr "File" + +#: views/campaigns/edit.hbs:32 +msgid "Size" +msgstr "Méret" + +#: 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 "Nincs adat a táblázatban." + +#: views/campaigns/edit.hbs:34 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "Csatolmány hozzáadása" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "Megnyitásokról információ" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "Feliratkozók, akik megnyitották ezt az üzenetet:" + +#: views/campaigns/opened.hbs:12 +msgid "First open" +msgstr "Első megnyitás" + +#: views/campaigns/opened.hbs:13 +msgid "Opened count" +msgstr "Megnyitások száma" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "Leiratkozásokról információ:" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "Feliratkozók, akik leiratkoztak:" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:28 +#: views/lists/subscription/import.hbs:13 routes/lists.js:206 +msgid "Unsubscribed" +msgstr "" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "Feltöltés" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "Áttekintés" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "Linkek" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "RSS státusz" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "Kampány előnezete, mint" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "Teszt felhasználó hozzáadása" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "Nincs még teszt felhasználó, hozzon létre egyet itt" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:26 +msgid "Delivered" +msgstr "Készbesítve" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "Azon felhasználók listázása, akik megkapták ezt az üzenetet" + +#: views/campaigns/view.hbs:22 +msgid "Blacklisted" +msgstr "Feketelistán szereplők" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who blacklisted by global blacklist" +msgstr "" +"Azon felhasználók listázása, akik feketelistára kerültek, mert a globális " +"feketelistán szerepeltek" + +#: views/campaigns/view.hbs:24 routes/lists.js:206 +msgid "Bounced" +msgstr "Visszapattant" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who bounced" +msgstr "" + +#: views/campaigns/view.hbs:26 +msgid "Complaints" +msgstr "" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who complained for this message" +msgstr "" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who unsubscribed after this message" +msgstr "" + +#: views/campaigns/view.hbs:30 +msgid "Opened" +msgstr "" + +#: views/campaigns/view.hbs:31 +msgid "List subscribers who opened this message" +msgstr "" + +#: views/campaigns/view.hbs:32 +msgid "Clicked" +msgstr "" + +#: views/campaigns/view.hbs:33 views/campaigns/view.hbs:70 +msgid "List subscribers who clicked on a link" +msgstr "" + +#: views/campaigns/view.hbs:34 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" + +#: views/campaigns/view.hbs:35 +msgid "Delay sending" +msgstr "" + +#: views/campaigns/view.hbs:36 +msgid "hours" +msgstr "óra" + +#: views/campaigns/view.hbs:37 +msgid "minutes" +msgstr "perc" + +#: views/campaigns/view.hbs:38 +msgid "Send to subscribers:" +msgstr "" + +#: views/campaigns/view.hbs:39 +msgid "Are you sure? This action would reset scheduling" +msgstr "" + +#: views/campaigns/view.hbs:40 +msgid "Cancel" +msgstr "Mégse" + +#: views/campaigns/view.hbs:41 +msgid "Sending scheduled" +msgstr "Küldés időzítve" + +#: views/campaigns/view.hbs:42 views/campaigns/view.hbs:54 +msgid "Pause" +msgstr "" + +#: views/campaigns/view.hbs:43 routes/campaigns.js:253 +msgid "Sending" +msgstr "Küldés folyamatban" + +#: 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 "" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" + +#: views/campaigns/view.hbs:46 +msgid "Resume" +msgstr "Folytatás" + +#: views/campaigns/view.hbs:47 views/campaigns/view.hbs:51 +msgid "Reset" +msgstr "" + +#: views/campaigns/view.hbs:50 +msgid "Continue" +msgstr "Tovább" + +#: views/campaigns/view.hbs:52 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" + +#: 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 "" + +#: views/campaigns/view.hbs:55 views/campaigns/view.hbs:59 +msgid "Campaign status:" +msgstr "Kampány státusza" + +#: views/campaigns/view.hbs:56 +msgid "ACTIVE" +msgstr "AKTÍV" + +#: 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 "" + +#: views/campaigns/view.hbs:58 +msgid "Activate" +msgstr "Aktív" + +#: views/campaigns/view.hbs:60 +msgid "INACTIVE" +msgstr "INAKTÍV" + +#: 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 "" + +#: views/campaigns/view.hbs:62 +msgid "see more" +msgstr "" + +#: views/campaigns/view.hbs:67 +msgid "List subscribers who clicked this link" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "Jelszó megváltoztatása" + +#: 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 "" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "" + +#: 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 "" + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "Beállítások" + +#: 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 "Leiratkozás" + +#: 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 "Üzenet megtekintése böngészőben" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "Kedves [FIRST_NAME/Customer]!" + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "Üdvözlettel," + +#: views/index.hbs:1 +msgid "List Management" +msgstr "A lista kezelői" + +#: 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 "" + +#: 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 "" + +#: 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 "Egyedi mezők" + +#: 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 "" + +#: views/index.hbs:6 +msgid "Mailtrain also supports custom forms." +msgstr "" + +#: views/index.hbs:8 +msgid "List Segmentation" +msgstr "" + +#: 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 "" + +#: views/index.hbs:11 +msgid "RSS Campaigns" +msgstr "RSS kampányok" + +#: 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 "" + +#: views/index.hbs:14 +msgid "GPG Encryption" +msgstr "GPG titkosítás" + +#: 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 "" + +#: views/index.hbs:17 +msgid "Click Stats" +msgstr "" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" + +#: views/index.hbs:20 +msgid "Template Editors" +msgstr "" + +#: 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 "" + +#: views/index.hbs:23 +msgid "Send via Any Provider" +msgstr "" + +#: views/index.hbs:24 +msgid "" +"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 "" + +#: views/index.hbs:26 lib/tools.js:137 +msgid "Automation" +msgstr "" + +#: views/index.hbs:27 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "" + +#: 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 "Fiók" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "Beállítások" + +#: 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 "Kijelentkezés" + +#: 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 "Bejelentkezés" + +#: views/layout.hbs:11 +msgid "Self Hosted Newsletter App Built on Top of Nodemailer" +msgstr "" + +#: views/layout.hbs:12 views/layout.hbs:14 +msgid "Source on GitHub" +msgstr "Forráskód a GitHub-on" + +#: views/layout.hbs:13 +msgid "Subscribe to Our Newsletter" +msgstr "Feliratkozás hírlevelünkre" + +#: 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 "Listák" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:13 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "Lista létrehozása" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "Lista neve" + +#: views/lists/create.hbs:9 views/lists/edit.hbs:15 +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "Feliratkozás" + +#: views/lists/create.hbs:10 views/lists/edit.hbs:16 +msgid "Allow public users to subscribe themselves" +msgstr "Nyilvános űrlapról való feliratkozás engedélyezése" + +#: views/lists/create.hbs:11 views/lists/edit.hbs:17 +msgid "Unsubscription" +msgstr "Leiratkozás" + +#: views/lists/create.hbs:12 views/lists/edit.hbs:18 +msgid "Select how an unsuscription request by subscriber is handled." +msgstr "" + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:8 +msgid "Edit List" +msgstr "Lista szerkesztése" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "Lista megtekintése" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "Lista ID" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "" + +#: views/lists/edit.hbs:12 +msgid "Custom Form" +msgstr "Egyedi űrlap" + +#: views/lists/edit.hbs:13 views/lists/forms/forms.hbs:11 +msgid "Default Mailtrain Form" +msgstr "Alapértelmezett Mailtrain űrlap" + +#: views/lists/edit.hbs:14 +msgid "" +"The custom form used for this list. You can create a form here." +msgstr "" +"Egyedi űrlap ehhez a listához. Itt " +"hozhat létre új űrlapot." + +#: views/lists/edit.hbs:19 +msgid "Unsubscribe Header" +msgstr "Leiratkozás fejléc" + +#: views/lists/edit.hbs:20 +msgid "Do not send List-Unsubscribe headers" +msgstr "Leiratkozás fejléc küldésének letiltása" + +#: views/lists/edit.hbs:21 +msgid "Delete List" +msgstr "Lista törlése" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "Mező létrehozása" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "Egyedi mező létrehozása" + +#: 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 "Mező neve" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "Mező típusa" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "Szöveg" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "Szám" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "Weboldal" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "GPG publikus kulcs" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "Többsoros szöveg" + +#: 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 "Dátum" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "Dátum (HH/NN/ÉÉÉÉ)" + +#: views/lists/fields/create.hbs:17 views/lists/fields/edit.hbs:18 +#: lib/models/fields.js:26 +msgid "Date (DD/MM/YYYY)" +msgstr "Dátum (NN/HH/ÉÉÉÉ)" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "Születésnap" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "Születésnap (HH/NN)" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "Születésnap (NN/HH)" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "Csoportosított" + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "Lenyíló menük" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "Rádió gombok" + +#: views/lists/fields/create.hbs:24 views/lists/fields/edit.hbs:25 +#: lib/models/fields.js:23 +msgid "Checkboxes" +msgstr "Jelölőmezők" + +#: views/lists/fields/create.hbs:25 views/lists/fields/edit.hbs:26 +msgid "Option for a group value" +msgstr "" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "Csoport" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/lists/fields/create.hbs:33 views/lists/fields/edit.hbs:37 +msgid "Visible" +msgstr "Látható" + +#: views/lists/fields/create.hbs:34 +msgid "Add Field" +msgstr "Mező hozzáadása" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "Mező szerkesztése" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "" + +#: 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 "" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "Mező törlése" + +#: views/lists/fields/fields.hbs:7 views/lists/view.hbs:26 +#: views/report-templates/partials/report-template-fields.hbs:5 +msgid "Type" +msgstr "Típus" + +#: 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:183 routes/triggers.js:297 +msgid "Edit" +msgstr "Szerkesztés" + +#: 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 "" + +#: views/lists/forms/create.hbs:4 +msgid "Create Form" +msgstr "" + +#: views/lists/forms/create.hbs:5 views/lists/forms/forms.hbs:4 +msgid "Create Custom Form" +msgstr "" + +#: 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 "" + +#: views/lists/forms/create.hbs:8 +msgid "Add Form" +msgstr "" + +#: views/lists/forms/edit.hbs:4 +msgid "Edit Form" +msgstr "" + +#: views/lists/forms/edit.hbs:5 +msgid "Edit Custom Form" +msgstr "" + +#: views/lists/forms/edit.hbs:6 +msgid "Back to forms" +msgstr "" + +#: views/lists/forms/edit.hbs:10 +msgid "Optional comments about this form" +msgstr "" + +#: views/lists/forms/edit.hbs:11 +msgid "Form Preview" +msgstr "Űrlap előnézete" + +#: 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 "" + +#: 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 "Feliratkozás" + +#: views/lists/forms/edit.hbs:14 +msgid "Confirm Subscription Notice" +msgstr "" + +#: views/lists/forms/edit.hbs:15 +msgid "Confirm Unsubscription Notice" +msgstr "" + +#: views/lists/forms/edit.hbs:16 +msgid "Subscribed Notice" +msgstr "" + +#: views/lists/forms/edit.hbs:17 +msgid "Updated Notice" +msgstr "" + +#: views/lists/forms/edit.hbs:18 +msgid "Unsubscribed Notice" +msgstr "" + +#: views/lists/forms/edit.hbs:19 +msgid "Manual Unsubscribe Notice" +msgstr "" + +#: views/lists/forms/edit.hbs:21 routes/forms.js:205 +msgid "Manage" +msgstr "" + +#: views/lists/forms/edit.hbs:22 +msgid "Manage Address" +msgstr "" + +#: views/lists/forms/edit.hbs:23 +msgid "Create a test user for additional options" +msgstr "" + +#: 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 "Sablonok" + +#: views/lists/forms/edit.hbs:25 +msgid "Fields" +msgstr "Mezők" + +#: views/lists/forms/edit.hbs:27 +msgid "Form Fields" +msgstr "Űrlap mezők" + +#: views/lists/forms/edit.hbs:28 +msgid "Fields hidden on subscription page:" +msgstr "" + +#: views/lists/forms/edit.hbs:29 +msgid "Fields shown on subscription page:" +msgstr "" + +#: views/lists/forms/edit.hbs:30 +msgid "Fields hidden on preferences page:" +msgstr "" + +#: views/lists/forms/edit.hbs:31 +msgid "Fields shown on preferences page:" +msgstr "" + +#: views/lists/forms/edit.hbs:32 +msgid "Delete Form" +msgstr "Űrlap törlése" + +#: views/lists/forms/forms.hbs:10 +msgid "The default form for this list is:" +msgstr "" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "ID" + +#: views/lists/lists.hbs:7 views/reports/partials/report-fields.hbs:10 +msgid "Subscribers" +msgstr "Feliratkozók" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "" + +#: 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 "" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "" + +#: 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 "Szabály létrehozása" + +#: 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 "Szabály" + +#: 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 "Érték" + +#: 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 "" + +#: 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 "Pontos találat használata" + +#: 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 "Tartomány találat használata" + +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-edit.hbs:20 +msgid "Use relative range match" +msgstr "Relatív tartomány találat használata" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: 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 "" + +#: views/lists/segments/rule-configure.hbs:33 +msgid "Add Rule" +msgstr "" + +#: views/lists/segments/rule-create.hbs:8 +#: views/lists/subscription/import.hbs:18 +#: views/reports/create-select-template.hbs:5 +#: views/triggers/create-select.hbs:9 +msgid "Next" +msgstr "Következő" + +#: views/lists/segments/rule-edit.hbs:37 +msgid "Delete Rule" +msgstr "" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "" + +#: views/lists/segments/view.hbs:5 views/lists/view.hbs:13 +msgid "Segment" +msgstr "" + +#: views/lists/segments/view.hbs:7 +msgid "Match rules" +msgstr "" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "Feliratkozó hozzáadása" + +#: views/lists/subscription/add.hbs:5 +#: views/subscription/partials/subscription-custom-fields.hbs:1 +#: views/users/account.hbs:7 +msgid "Email Address" +msgstr "Email cím" + +#: views/lists/subscription/add.hbs:8 views/lists/subscription/edit.hbs:9 +#: views/settings.hbs:80 views/settings.hbs:95 +#: views/subscription/partials/subscription-custom-fields.hbs:6 +msgid "Begins with" +msgstr "" + +#: 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 "" +"Adjon meg egy GPG publikus kulcsot, amivel az üzenetek titkosítva lesznek " +"küldéskor" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "Időzóna" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "Teszt felhasználó?" + +#: 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 "" + +#: 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 "" + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "Feliratkozó szerkesztése" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "Vissza a listához" + +#: 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 "Email cím" + +#: views/lists/subscription/edit.hbs:17 +msgid "Delete Subscription" +msgstr "Feliratkozás törlése" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "Importálás státusza" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "Hibás címek" + +#: 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 "" + +#: views/lists/subscription/import-failed.hbs:7 +msgid "see here" +msgstr "" + +#: views/lists/subscription/import-failed.hbs:9 +msgid "Fail reason" +msgstr "Hiba oka" + +#: 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 "Feliratkozók imprtálása" + +#: views/lists/subscription/import-preview.hbs:10 views/users/api.hbs:21 +#: views/users/api.hbs:36 views/users/api.hbs:44 views/users/api.hbs:52 +#: views/users/api.hbs:64 views/users/api.hbs:75 views/users/api.hbs:83 +#: views/users/api.hbs:91 views/users/api.hbs:96 views/users/api.hbs:101 +#: views/users/api.hbs:106 +msgid "Example" +msgstr "Példa" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "Importálás megkezdése" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "CSV file" + +#: views/lists/subscription/import.hbs:6 +msgid "The first line must contain column headings" +msgstr "Az első sor fejlécet kell, hogy tartalmazzon" + +#: views/lists/subscription/import.hbs:7 +msgid "CSV delimiter" +msgstr "CSV elválasztó" + +#: views/lists/subscription/import.hbs:8 +msgid "Categorize the imported subscribers as" +msgstr "" + +#: views/lists/subscription/import.hbs:9 routes/lists.js:206 +msgid "Subscribed" +msgstr "" + +#: views/lists/subscription/import.hbs:10 +msgid "Regular subscriber addresses" +msgstr "" + +#: views/lists/subscription/import.hbs:11 +msgid "Subscribed (Force)" +msgstr "" + +#: views/lists/subscription/import.hbs:12 +msgid "Regular subscriber addresses, resubscribe users that have unsubscribed" +msgstr "" + +#: views/lists/subscription/import.hbs:14 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "" + +#: views/lists/subscription/import.hbs:15 +msgid "Check imported emails" +msgstr "" + +#: views/lists/subscription/import.hbs:16 views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "" + +#: views/lists/subscription/import.hbs:17 views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "Feliratkozós űrlap" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "Lista műveletek" + +#: 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 "" + +#: views/lists/view.hbs:10 +msgid "Add Subscriber" +msgstr "Feliratkozó hozzáadása" + +#: views/lists/view.hbs:11 +msgid "Import Subscribers" +msgstr "Feliratkozók importálása" + +#: views/lists/view.hbs:15 +msgid "Create New Segment" +msgstr "" + +#: views/lists/view.hbs:16 +msgid "Filter" +msgstr "Szűrés" + +#: views/lists/view.hbs:17 +msgid "Subscriptions" +msgstr "" + +#: views/lists/view.hbs:18 +msgid "Imports" +msgstr "" + +#: views/lists/view.hbs:25 routes/campaigns.js:255 routes/lists.js:300 +msgid "Finished" +msgstr "Befejezve" + +#: views/lists/view.hbs:27 +msgid "Added" +msgstr "Hozzáadva" + +#: views/lists/view.hbs:28 +msgid "Updated" +msgstr "Frissítve" + +#: views/lists/view.hbs:29 +msgid "Failed" +msgstr "" + +#: views/lists/view.hbs:31 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" + +#: views/lists/view.hbs:32 +msgid "Restart" +msgstr "Újrakezdés" + +#: views/mosaico/editor.hbs:1 views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "" + +#: views/mosaico/editor.hbs:4 +msgid "MOSAICO Responsive Email Designer" +msgstr "" + +#: views/mosaico/editor.hbs:5 +msgid "Sucessfully saved" +msgstr "Sikeres mentés" + +#: views/mosaico/editor.hbs:6 +msgid "An error occured while saving the document" +msgstr "A dokumentum mentése közben hiba lépett fel." + +#: views/mosaico/editor.hbs:7 +msgid "Unsaved changes will be lost. Close now?" +msgstr "Az el nem mentett változtatások el fognak veszni. Bezárja?" + +#: views/mosaico/editor.hbs:8 views/mosaico/editor.hbs:9 +msgid "Tags" +msgstr "Címkék" + +#: 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 "Sablon tartalma (HTML)" + +#: views/partials/editor-navbar.hbs:1 +msgid "SAVE" +msgstr "MENTÉS" + +#: views/partials/editor-navbar.hbs:2 +msgid "SAVING" +msgstr "MENTÉS FOLYMATBAN" + +#: views/partials/editor-navbar.hbs:3 +msgid "CLOSE" +msgstr "BEZÁRÁS" + +#: views/partials/grapejs.hbs:2 +msgid "Open GrapeJS" +msgstr "GrapeJS megnyitása" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "HTML előnézet átváltása" + +#: views/partials/html-to-text.hbs:1 +msgid "" +"To extract the text from HTML click here." +msgstr "" + +#: 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 "" + +#: views/partials/html-to-text.hbs:3 +msgid "An error occurred while talking to the server" +msgstr "" + +#: 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 "" + +#: views/partials/modal-carousel.hbs:1 +msgid "{{title}}" +msgstr "" + +#: views/partials/mosaico.hbs:2 +msgid "Open Mosaico" +msgstr "Mosaico megnyitása" + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "Sablon tartalma (sima szöveg)" + +#: 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 "Jelentések" + +#: 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 "Sablon létrehozása" + +#: views/report-templates/create.hbs:5 routes/report-templates.js:231 +msgid "Create Report Template" +msgstr "Jelentés sablon létrehozása" + +#: views/report-templates/edit.hbs:4 views/templates/edit.hbs:3 +#: views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "Sablon szerkesztése" + +#: views/report-templates/edit.hbs:5 routes/report-templates.js:262 +msgid "Edit Report Template" +msgstr "Jelentés sablon szerkesztése" + +#: views/report-templates/edit.hbs:6 views/templates/edit.hbs:12 +msgid "Delete Template" +msgstr "Sablon törlése" + +#: views/report-templates/edit.hbs:7 +msgid "Update and Stay" +msgstr "" + +#: views/report-templates/edit.hbs:8 +msgid "Update and Leave" +msgstr "" + +#: views/report-templates/partials/report-template-fields.hbs:2 +msgid "Template Name" +msgstr "Sablon neve" + +#: views/report-templates/partials/report-template-fields.hbs:6 +msgid "User selectable fields" +msgstr "" + +#: views/report-templates/partials/report-template-fields.hbs:7 +msgid "Data processing code" +msgstr "" + +#: views/report-templates/partials/report-template-fields.hbs:8 +msgid "Rendering template" +msgstr "" + +#: views/report-templates/report-templates.hbs:5 +msgid "Blank" +msgstr "" + +#: views/report-templates/report-templates.hbs:6 +msgid "All Subscribers" +msgstr "Összes feliratkozó" + +#: views/report-templates/report-templates.hbs:7 +msgid "Grouped Subscribers" +msgstr "" + +#: views/report-templates/report-templates.hbs:8 +msgid "Export List as CSV" +msgstr "Lista exportálása CSV-be" + +#: views/report-templates/report-templates.hbs:9 views/reports/reports.hbs:4 +#: routes/report-templates.js:29 +msgid "Report Templates" +msgstr "Jelentés sablonok" + +#: 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 "Jelentés létrehozása" + +#: views/reports/edit.hbs:3 views/reports/edit.hbs:4 routes/reports.js:151 +msgid "Edit Report" +msgstr "Jelentés szerkesztése" + +#: views/reports/edit.hbs:5 +msgid "Delete Report" +msgstr "Jelentés törlése" + +#: views/reports/partials/report-fields.hbs:2 +msgid "Report Name" +msgstr "Jelentés neve" + +#: 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 "" + +#: views/reports/partials/report-select-template.hbs:1 +msgid "Report Template" +msgstr "Jelentés sablon" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "Szolgáltatás címe (URL)" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "Adja meg az URL-t, amiről ez a szolgáltatás elérhető" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "Admin email" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "" + +#: 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 "" + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "Követő ID" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "Google Analytics követőkód megadása" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "" + +#: views/settings.hbs:17 +msgid "Campaign defaults" +msgstr "" + +#: views/settings.hbs:18 +msgid "Sender name" +msgstr "Küldő neve" + +#: views/settings.hbs:19 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "Küldő neve, pl.: Éncégem Kft." + +#: views/settings.hbs:20 +msgid "Default address" +msgstr "Alapértelmezett postai cím" + +#: views/settings.hbs:21 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "" + +#: views/settings.hbs:22 +msgid "Default \"from name\"" +msgstr "" + +#: views/settings.hbs:24 +msgid "Default \"from\" email" +msgstr "" + +#: views/settings.hbs:26 +msgid "Default \"subject line\"" +msgstr "" + +#: views/settings.hbs:28 +msgid "Default homepage (URL)" +msgstr "" + +#: views/settings.hbs:29 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "" + +#: views/settings.hbs:30 +msgid "Mailer Settings" +msgstr "" + +#: views/settings.hbs:31 +msgid "These settings are required to send out e-mail messages" +msgstr "" + +#: 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 "SMTP használata levélküldéshez" + +#: views/settings.hbs:35 +msgid "Hostname" +msgstr "" + +#: views/settings.hbs:36 +msgid "Port" +msgstr "Port" + +#: views/settings.hbs:37 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "" + +#: views/settings.hbs:38 +msgid "Encryption" +msgstr "Titkosítás" + +#: views/settings.hbs:39 +msgid "Disable SMTP authentication" +msgstr "SMTP azonosítás letiltása" + +#: views/settings.hbs:40 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "Felhasználónév" + +#: views/settings.hbs:41 +msgid "Username, eg. myaccount@example.com" +msgstr "Felhasználónév, pl.: myaccount@example.com" + +#: views/settings.hbs:42 views/settings.hbs:43 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "Jelszó" + +#: views/settings.hbs:44 +msgid "Use SES API for sending mail" +msgstr "SES API használata levélküldéshez" + +#: views/settings.hbs:45 +msgid "Access Key" +msgstr "" + +#: views/settings.hbs:46 +msgid "AWS Access Key Id" +msgstr "" + +#: views/settings.hbs:47 +msgid "Secret Key" +msgstr "" + +#: views/settings.hbs:48 +msgid "AWS Secret Access Key" +msgstr "" + +#: views/settings.hbs:49 +msgid "Region" +msgstr "Régió" + +#: views/settings.hbs:50 +msgid "Checking" +msgstr "" + +#: views/settings.hbs:51 +msgid "Check Mailer config" +msgstr "" + +#: views/settings.hbs:52 +msgid "Advanced Mailer settings" +msgstr "" + +#: views/settings.hbs:53 +msgid "Log SMTP transactions" +msgstr "SMTP tranzakciók naplózása" + +#: views/settings.hbs:54 +msgid "Allow self-signed certificates" +msgstr "" + +#: views/settings.hbs:55 +msgid "Max connections" +msgstr "" + +#: views/settings.hbs:56 +msgid "The count of max connections, eg. 10" +msgstr "" + +#: views/settings.hbs:57 +msgid "" +"The count of maximum simultaneous connections to make against the SMTP " +"server (defaults to 5). This limit is per sending process." +msgstr "" + +#: views/settings.hbs:58 +msgid "Max messages" +msgstr "" + +#: views/settings.hbs:59 +msgid "The count of max messages, eg. 100" +msgstr "" + +#: views/settings.hbs:60 +msgid "" +"The number of messages to send through a single connection before the " +"connection is closed and reopened (defaults to 100)" +msgstr "" + +#: views/settings.hbs:61 +msgid "Throttling" +msgstr "" + +#: views/settings.hbs:62 +msgid "Messages per hour eg. 1000" +msgstr "" + +#: views/settings.hbs:63 +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 "" + +#: views/settings.hbs:64 +msgid "VERP bounce handling" +msgstr "" + +#: views/settings.hbs:65 +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 "" + +#: views/settings.hbs:66 +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 "" + +#: views/settings.hbs:67 +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 "" + +#: views/settings.hbs:68 +msgid "Use VERP to catch bounces" +msgstr "" + +#: views/settings.hbs:69 +msgid "Server hostname" +msgstr "" + +#: views/settings.hbs:70 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "" + +#: views/settings.hbs:71 +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 "" + +#: views/settings.hbs:72 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" + +#: views/settings.hbs:73 +msgid "GPG Signing" +msgstr "GPG aláírás" + +#: views/settings.hbs:74 +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 "" + +#: views/settings.hbs:75 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" + +#: views/settings.hbs:76 +msgid "Private Key Passphrase" +msgstr "" + +#: views/settings.hbs:77 +msgid "Passphrase for the key if set" +msgstr "" + +#: views/settings.hbs:78 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "" + +#: views/settings.hbs:79 +msgid "GPG Private Key" +msgstr "GPG privát kulcs" + +#: views/settings.hbs:81 +msgid "" +"This value is optional. If you do not provide a private key GPG encrypted " +"messages are sent without signing." +msgstr "" + +#: views/settings.hbs:82 +msgid "DKIM Signing by ZoneMTA" +msgstr "" + +#: views/settings.hbs:83 +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 "" + +#: views/settings.hbs:84 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" + +#: views/settings.hbs:85 +msgid "ZoneMTA DKIM API Key" +msgstr "" + +#: views/settings.hbs:86 +msgid "Some secret value" +msgstr "" + +#: views/settings.hbs:87 +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 "" + +#: views/settings.hbs:88 +msgid "DKIM domain" +msgstr "" + +#: views/settings.hbs:89 +msgid "Domain name for the DKIM key" +msgstr "" + +#: views/settings.hbs:90 +msgid "Leave blank to use the sender email address domain" +msgstr "" + +#: views/settings.hbs:91 views/settings.hbs:92 +msgid "DKIM key selector" +msgstr "" + +#: views/settings.hbs:93 +msgid "Signing is disabled without a valid selector value" +msgstr "" + +#: views/settings.hbs:94 +msgid "DKIM Private Key" +msgstr "" + +#: views/settings.hbs:96 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:1 +#: views/subscription/mail-already-subscribed-text.hbs:1 +#: lib/models/subscriptions.js:174 lib/models/subscriptions.js:895 +msgid "Email address already registered" +msgstr "Ezt az email címet már regisztrálták." + +#: 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 "" +"Megkaptuk a feliratkozási kérelmét, viszont címe már eddig is regisztrálva " +"volt." + +#: 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 "" +"Ha véletlenül kapta ezt a levelet, egyszerűen törölje. A feliratkozás " +"státusza nem fog változni." + +#: 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 "Ha szeretné megváltoztatni feliratkozását, akkor" + +#: 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 "módosíthatja beállításait" + +#: 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 "vagy" + +#: 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 "leiratkozhat itt" + +#: 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 "Visszatérés az odalunkra" + +#: 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 "A listával kapcsolatos kérdéseivel itt tud megkeresni minket:" + +#: 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 "Ha szeretné megváltoztatni feliratkozását, akkor" + +#: 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 "Kérjük erősítse meg a feliratkozáshoz használt emailcímének változását" + +#: 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 "Igen, szeretnék feliratkozni a listára ezzel az email-lel." + +#: 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 "" +"Ha nem ön kérte ezt a levelet, nyugodtan törölheti. Ha nem kattint a fenti " +"linkre, továbbra is feliratkozva marad." + +#: 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 "" +"Ha nem ön kérte ezt a levelet, nyugodtan törölheti. Ha nem kattint a fenti " +"linkre, nem vesszük fel a listára." + +#: 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:432 +msgid "Please Confirm Subscription" +msgstr "Erősítse meg a feliratkozást" + +#: 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 "Igen, szeretnék feliratkozni a listára." + +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:1 +msgid "Please Confirm Unsubscription" +msgstr "Erősítse meg a leiratkozást" + +#: 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 "Igen, szeretnék leiratkozni a listáról." + +#: 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 "" +"Ha nem ön kérte ezt a levelet, nyugodtan törölheti. Ha nem kattint a fenti " +"linkre, továbbra is feliratkozva marad." + +#: 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 "" +"Ha nem ön kérte ezt a levelet, nyugodtan törölheti. Ha nem kattint a fenti " +"linkre, továbbra is feliratkozva marad." + +#: 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 "Feliratkozás megerősítve" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed" +msgstr "A listára való feliratkozása megerősítve." + +#: 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 "A listára való feliratkozása megerősítve." + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:1 +msgid "You Are Now Unsubscribed" +msgstr "Leiratkozott a listáról." + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:2 +msgid "We have removed your email address from our list" +msgstr "Email címét eltávolítottuk a listánkról." + +#: 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 "Ha véletlenül iratkozott le, újra feliratkozhat itt:" + +#: views/subscription/mail-unsubscription-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "Email címét eltávolítottuk a listánkról." + +#: views/subscription/partials/subscription-custom-fields.hbs:2 +msgid "want to change it?" +msgstr "meg akarja változtatni?" + +#: views/subscription/partials/subscription-custom-fields.hbs:5 +msgid "Download signature verification key" +msgstr "" + +#: views/subscription/partials/subscription-custom-fields.hbs:7 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "Adja meg publikus GPG kulcsát, hogy kódolt üzeneteket küldhessünk" + +#: views/subscription/partials/subscription-custom-fields.hbs:8 +msgid "optional" +msgstr "nem kötelező" + +#: views/subscription/partials/subscription-flash-messages.hbs:1 +#: views/subscription/partials/subscription-flash-messages.hbs:3 +msgid "Warning!" +msgstr "Figyelem!" + +#: views/subscription/partials/subscription-flash-messages.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "" +"Ha a JavaScript nem volt engedélyezve, akkor nem küldünk megerősítő levelet." + +#: views/subscription/partials/subscription-flash-messages.hbs:4 +msgid "JavaScript must be enabled in order for this form to work" +msgstr "Engedélyezni kell a JavaScriptet az űrlap használatához." + +#: views/subscription/partials/subscription-manage-address-form.hbs:1 +msgid "Existing Email Address" +msgstr "Eddigi email cím" + +#: views/subscription/partials/subscription-manage-address-form.hbs:2 +msgid "New Email Address" +msgstr "Új email cím" + +#: views/subscription/partials/subscription-manage-address-form.hbs:3 +msgid "Your new email address" +msgstr "Az ön új email címe" + +#: 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 "" +"Küldünk egy megerősítést kérő üzenetet az új email címére. Az új cím csak " +"akkor lép érvénybe, ha az üzenetben rákattint a megerősítésre." + +#: views/subscription/partials/subscription-manage-address-form.hbs:5 +#: views/subscription/web-manage-address.mjml.hbs:2 +msgid "Update Email Address" +msgstr "Email cím cseréje" + +#: views/subscription/partials/subscription-manage-form.hbs:1 +#: views/subscription/web-manage.mjml.hbs:2 +msgid "Update Profile" +msgstr "Profil frissítése" + +#: 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 "Feliratkozás" + +#: 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 "Majdnem kész" + +#: 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 "" +"Meg kell erősítenie email címét. A feliratkozás befejezéséhez kattintson a " +"linkre, amit a megerősítést kérő üzenetben küldtünk." + +#: 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 "" +"Meg kell erősítenie email címét. A leiratkozás befejezéséhez kattintson a " +"linkre, amit a megerősítést kérő üzenetben küldtünk." + +#: views/subscription/web-manage-address.mjml.hbs:1 +msgid "Update Your Email Address" +msgstr "Email cím frissítése" + +#: views/subscription/web-manage.mjml.hbs:1 +msgid "Update Your Preferences" +msgstr "Beállítások frissítése" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:1 +msgid "Online Unsubscription Is Not Possible" +msgstr "Nem lehetséges az online feliratkozás." + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:2 +msgid "Please contact us at" +msgstr "Vegye fel velünk a kapcsolatot itt:" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:3 +msgid "to get removed from the list" +msgstr "hogy eltávolítsuk a listáról." + +#: views/subscription/web-subscribe.mjml.hbs:1 +msgid "Subscribe to List" +msgstr "Feliratkozás a listára" + +#: views/subscription/web-subscribed-notice.mjml.hbs:3 +msgid "Thank you for subscribing!" +msgstr "Köszönjük, hogy feliratkozott!" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:1 +msgid "Unsubscribe Successful" +msgstr "Sikeresen leiratkozott." + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:2 +msgid "You have been removed from:" +msgstr "El lett távolítva innen:" + +#: views/subscription/web-updated-notice.mjml.hbs:1 +msgid "Profile Updated" +msgstr "Profil frissítve." + +#: views/subscription/web-updated-notice.mjml.hbs:2 +msgid "Your profile information has been updated." +msgstr "A profilja frissült." + +#: views/subscription/widget-subscribe.hbs:2 +msgid "Sending ..." +msgstr "Küldés..." + +#: views/subscription/widget-subscribe.hbs:3 +msgid "It looks like you are already subscribed to this list." +msgstr "Úgy tűnik, korábban már feliratkozott a listára." + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "Sablon neve" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "" + +#: views/templates/create.hbs:7 +msgid "HTML Editor" +msgstr "HTML szerkesztő" + +#: views/templates/create.hbs:10 views/templates/edit.hbs:9 +msgid "Optional comments about this template" +msgstr "" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "" + +#: views/templates/edit.hbs:11 +msgid "Duplicate" +msgstr "Duplikálás" + +#: 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 "" + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "" + +#: views/triggers/create.hbs:16 views/triggers/create.hbs:21 +#: views/triggers/edit.hbs:18 views/triggers/edit.hbs:23 +msgid "Event" +msgstr "Esemény" + +#: 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 "Kampány" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "" + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you lose your password" +msgstr "" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "" + +#: views/users/account.hbs:16 views/users/reset.hbs:8 +msgid "Confirm Password" +msgstr "" + +#: views/users/account.hbs:17 views/users/reset.hbs:9 +msgid "Confirm New Password" +msgstr "" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "" + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: views/users/api.hbs:13 +msgid "Get list of subscriptions" +msgstr "" + +#: views/users/api.hbs:14 +msgid "" +"Retrieve a list of subscriptions to the list referenced by :listId. All " +"fields of the subscribers will be returned. Note that custom fields will " +"have generated names." +msgstr "" + +#: views/users/api.hbs:15 views/users/api.hbs:24 views/users/api.hbs:26 +#: views/users/api.hbs:39 views/users/api.hbs:41 views/users/api.hbs:47 +#: views/users/api.hbs:49 views/users/api.hbs:55 views/users/api.hbs:57 +#: views/users/api.hbs:67 views/users/api.hbs:78 views/users/api.hbs:80 +#: views/users/api.hbs:86 views/users/api.hbs:88 views/users/api.hbs:94 +#: views/users/api.hbs:99 views/users/api.hbs:104 +msgid "arguments" +msgstr "" + +#: views/users/api.hbs:16 views/users/api.hbs:25 views/users/api.hbs:40 +#: views/users/api.hbs:48 views/users/api.hbs:56 views/users/api.hbs:68 +#: views/users/api.hbs:79 views/users/api.hbs:87 views/users/api.hbs:95 +#: views/users/api.hbs:100 views/users/api.hbs:105 +msgid "your personal access token" +msgstr "" + +#: views/users/api.hbs:17 views/users/api.hbs:69 +msgid "Start position" +msgstr "" + +#: views/users/api.hbs:18 views/users/api.hbs:70 +msgid "optional, default 0" +msgstr "" + +#: views/users/api.hbs:19 +msgid "limit subscription count in response" +msgstr "" + +#: views/users/api.hbs:20 views/users/api.hbs:72 +msgid "optional, default 10000" +msgstr "" + +#: views/users/api.hbs:22 +msgid "Add subscription" +msgstr "" + +#: views/users/api.hbs:23 +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 "" + +#: views/users/api.hbs:27 views/users/api.hbs:42 views/users/api.hbs:50 +msgid "subscriber's email address" +msgstr "" + +#: views/users/api.hbs:28 views/users/api.hbs:43 views/users/api.hbs:51 +#: views/users/api.hbs:59 views/users/api.hbs:82 views/users/api.hbs:90 +msgid "required" +msgstr "" + +#: views/users/api.hbs:29 +msgid "subscriber's first name" +msgstr "feliratkozó keresztneve" + +#: views/users/api.hbs:30 +msgid "subscriber's last name" +msgstr "feliratkozó vezetékneve" + +#: views/users/api.hbs:31 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" +"Feliratkozó időzónája (pl.: \"Europe/Budapest\", \"CEST\", \"UTC\"). " +"Alapértelmezés szerint \"UTC\"." + +#: views/users/api.hbs:32 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" + +#: views/users/api.hbs:33 +msgid "Additional POST arguments" +msgstr "" + +#: views/users/api.hbs:34 +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 "" + +#: views/users/api.hbs:35 +msgid "" +"set to \"yes\" if you want to send confirmation email to the subscriber " +"before actually marking as subscribed" +msgstr "" + +#: views/users/api.hbs:37 +msgid "Remove subscription" +msgstr "" + +#: views/users/api.hbs:38 +msgid "This API call marks a subscription as unsubscribed" +msgstr "" + +#: views/users/api.hbs:45 +msgid "Delete subscription" +msgstr "" + +#: views/users/api.hbs:46 +msgid "This API call deletes a subscription" +msgstr "" + +#: views/users/api.hbs:53 +msgid "Add new custom field" +msgstr "" + +#: views/users/api.hbs:54 +msgid "This API call creates a new custom field for a list." +msgstr "" + +#: views/users/api.hbs:58 +msgid "field name" +msgstr "" + +#: views/users/api.hbs:60 +msgid "one of the following types:" +msgstr "" + +#: views/users/api.hbs:61 +msgid "" +"If the type is 'option' then you also need to specify the parent element ID" +msgstr "" + +#: views/users/api.hbs:62 +msgid "" +"Template for the group element. If not set, then values of the elements are " +"joined with commas" +msgstr "" + +#: views/users/api.hbs:63 +msgid "" +"if not visible then the subscriber can not view or modify this value at the " +"profile page" +msgstr "" + +#: views/users/api.hbs:65 +msgid "Get list of blacklisted emails" +msgstr "" + +#: views/users/api.hbs:66 +msgid "This API call get list of blacklisted emails." +msgstr "" + +#: views/users/api.hbs:71 +msgid "limit emails count in response" +msgstr "" + +#: views/users/api.hbs:73 +msgid "filter by part of email" +msgstr "" + +#: views/users/api.hbs:74 +msgid "optional, default ''" +msgstr "" + +#: views/users/api.hbs:77 +msgid "This API call either add emails to blacklist" +msgstr "" + +#: views/users/api.hbs:81 views/users/api.hbs:89 +msgid "email address" +msgstr "" + +#: views/users/api.hbs:84 +msgid "Delete email from blacklist" +msgstr "" + +#: views/users/api.hbs:85 +msgid "This API call either delete emails from blacklist" +msgstr "" + +#: views/users/api.hbs:92 +msgid "Get the lists a user has subscribed to" +msgstr "" + +#: views/users/api.hbs:93 +msgid "Retrieve the lists that the user with :email has subscribed to. " +msgstr "" + +#: views/users/api.hbs:97 +msgid "Get all lists" +msgstr "" + +#: views/users/api.hbs:98 +msgid "Retrieve every list. " +msgstr "" + +#: views/users/api.hbs:102 +msgid "Get list by id" +msgstr "" + +#: views/users/api.hbs:103 +msgid "Retrieve the list with :id " +msgstr "" + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "" + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "" + +#: 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 "" + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "" + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "Emlékezzen rám" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "Elfelejtett jelszó" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "" + +#: lib/editor-helpers.js:17 routes/templates.js:95 +msgid "Could not find template with specified ID" +msgstr "" + +#: 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 "" + +#: lib/editor-helpers.js:47 routes/editorapi.js:320 +msgid "Invalid resource type" +msgstr "" + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "" + +#: lib/helpers.js:33 +msgid "URL that points to the unsubscribe page" +msgstr "" + +#: lib/helpers.js:36 +msgid "URL that points to the preferences page of the subscriber" +msgstr "" + +#: lib/helpers.js:39 +msgid "URL to preview the message in a browser" +msgstr "" + +#: lib/helpers.js:45 lib/models/segments.js:31 +msgid "First name" +msgstr "Keresztnév" + +#: lib/helpers.js:48 lib/models/segments.js:35 +msgid "Last name" +msgstr "Vezetéknév" + +#: lib/helpers.js:51 +msgid "Full name (first and last name combined)" +msgstr "Teljes név (vezetéknév + keresztnév)" + +#: lib/helpers.js:54 +msgid "Unique ID that identifies the recipient" +msgstr "" + +#: lib/helpers.js:57 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "" + +#: lib/helpers.js:60 +msgid "Unique ID that identifies current campaign" +msgstr "" + +#: lib/helpers.js:68 lib/helpers.js:80 +msgid "content from an RSS entry" +msgstr "" + +#: lib/helpers.js:71 +msgid "RSS entry title" +msgstr "" + +#: lib/helpers.js:74 +msgid "RSS entry date" +msgstr "" + +#: lib/helpers.js:77 +msgid "RSS entry link" +msgstr "" + +#: lib/helpers.js:83 +msgid "RSS entry summary" +msgstr "" + +#: lib/helpers.js:86 +msgid "RSS entry image URL" +msgstr "" + +#: lib/mailer.js:245 +msgid "Invalid mail transport" +msgstr "" + +#: 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 "" + +#: lib/models/campaigns.js:241 +msgid "Emtpy or too large attahcment" +msgstr "" + +#: lib/models/campaigns.js:408 lib/models/campaigns.js:600 +msgid "Campaign Name must be set" +msgstr "" + +#: lib/models/campaigns.js:412 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "" + +#: lib/models/campaigns.js:568 +msgid "Selected template not found" +msgstr "" + +#: lib/models/campaigns.js:924 +msgid "Invalid or missing message ID" +msgstr "" + +#: lib/models/campaigns.js:1065 +msgid "Unrecognized message status" +msgstr "" + +#: lib/models/confirmations.js:27 +msgid "Could not store confirmation data" +msgstr "" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "Dátum (NN/HH/ÉÉÉÉ)" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/forms.js:46 lib/models/lists.js:115 lib/models/lists.js:150 +#: lib/models/lists.js:265 lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:79 lib/models/subscriptions.js:390 +#: lib/models/subscriptions.js:566 lib/models/subscriptions.js:657 +#: lib/models/subscriptions.js:710 lib/models/subscriptions.js:773 +#: lib/models/subscriptions.js:816 +msgid "Missing List ID" +msgstr "" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "" + +#: 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 "" + +#: lib/models/forms.js:121 lib/models/forms.js:205 +msgid "Form Name must be set" +msgstr "" + +#: lib/models/forms.js:307 +msgid "Custom form not found" +msgstr "" + +#: 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 "" + +#: lib/models/links.js:345 routes/lists.js:181 services/sender.js:312 +msgid "List not found" +msgstr "Lista nem található." + +#: lib/models/links.js:353 +msgid "Subscription not found" +msgstr "" + +#: lib/models/lists.js:167 lib/models/lists.js:211 +msgid "List Name must be set" +msgstr "" + +#: lib/models/lists.js:294 +msgid "Missing List CID" +msgstr "" + +#: lib/models/report-templates.js:26 lib/models/report-templates.js:70 +#: lib/models/report-templates.js:142 +msgid "Missing report template ID" +msgstr "" + +#: lib/models/report-templates.js:77 +msgid "Report template name must be set" +msgstr "" + +#: lib/models/reports.js:40 lib/models/reports.js:110 lib/models/reports.js:188 +msgid "Missing report ID" +msgstr "" + +#: lib/models/reports.js:116 +msgid "Report name must be set" +msgstr "" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "" + +#: lib/models/segments.js:19 lib/models/triggers.js:12 +msgid "Sign up date" +msgstr "Feliratkozás ideje" + +#: lib/models/segments.js:23 lib/models/triggers.js:16 +msgid "Latest open" +msgstr "" + +#: lib/models/segments.js:27 lib/models/triggers.js:20 +msgid "Latest click" +msgstr "" + +#: 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 "" + +#: lib/models/segments.js:85 lib/models/segments.js:549 +#: lib/models/segments.js:658 +msgid "Segment not found" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: lib/models/segments.js:148 lib/models/segments.js:410 +msgid "today" +msgstr "" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: lib/models/segments.js:358 lib/models/segments.js:434 +#: lib/models/segments.js:524 +msgid "Missing Rule ID" +msgstr "" + +#: lib/models/segments.js:374 +msgid "Specified rule not found" +msgstr "" + +#: lib/models/segments.js:385 +msgid "Specified segment not found" +msgstr "" + +#: lib/models/segments.js:445 +msgid "Selected rule not found" +msgstr "" + +#: lib/models/subscriptions.js:257 lib/models/subscriptions.js:287 +#: lib/models/subscriptions.js:394 +msgid "Missing Subscription ID" +msgstr "" + +#: lib/models/subscriptions.js:315 +msgid "Missing Subscription email address" +msgstr "" + +#: lib/models/subscriptions.js:570 lib/models/subscriptions.js:820 +msgid "Missing subscription ID" +msgstr "" + +#: lib/models/subscriptions.js:661 lib/models/subscriptions.js:714 +#: lib/models/subscriptions.js:750 +msgid "Missing Import ID" +msgstr "" + +#: lib/models/subscriptions.js:842 +msgid "Unknown subscription ID" +msgstr "" + +#: lib/models/subscriptions.js:847 routes/subscription.js:639 +msgid "Nothing seems to be changed" +msgstr "" + +#: lib/models/subscriptions.js:913 routes/subscription.js:473 +#: routes/subscription.js:545 routes/subscription.js:581 +#: routes/subscription.js:697 routes/subscription.js:772 +msgid "Subscription not found in this list" +msgstr "" + +#: lib/models/templates.js:26 lib/models/templates.js:102 +#: lib/models/templates.js:157 +msgid "Missing Template ID" +msgstr "" + +#: lib/models/templates.js:55 lib/models/templates.js:106 +msgid "Template Name must be set" +msgstr "" + +#: lib/models/templates.js:147 +msgid "Template does not exist" +msgstr "" + +#: lib/models/triggers.js:29 +msgid "Has Opened" +msgstr "" + +#: lib/models/triggers.js:32 +msgid "Has Clicked" +msgstr "" + +#: lib/models/triggers.js:35 +msgid "Not Opened" +msgstr "" + +#: lib/models/triggers.js:38 +msgid "Not Clicked" +msgstr "" + +#: lib/models/triggers.js:178 lib/models/triggers.js:215 +msgid "Missing or invalid list ID" +msgstr "" + +#: lib/models/triggers.js:182 lib/models/triggers.js:267 +msgid "Days in the past are not allowed" +msgstr "" + +#: lib/models/triggers.js:186 lib/models/triggers.js:207 +#: lib/models/triggers.js:271 lib/models/triggers.js:292 +msgid "Missing or invalid trigger rule" +msgstr "" + +#: lib/models/triggers.js:193 lib/models/triggers.js:278 +msgid "Invalid subscription configuration" +msgstr "" + +#: lib/models/triggers.js:200 lib/models/triggers.js:285 +msgid "Invalid campaign configuration" +msgstr "" + +#: lib/models/triggers.js:203 lib/models/triggers.js:288 +msgid "A campaing can not be a target for itself" +msgstr "" + +#: lib/models/triggers.js:236 +msgid "Could not store trigger row" +msgstr "" + +#: lib/models/triggers.js:253 +msgid "Missing or invalid Trigger ID" +msgstr "" + +#: lib/models/triggers.js:320 +msgid "Missing Trigger ID" +msgstr "" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "Érvénytelen új jelszó" + +#: lib/passport.js:49 +msgid "%s logged out" +msgstr "%s kijelentkezett" + +#: lib/passport.js:64 +msgid "Failed to authenticate user" +msgstr "Felhasználó azonosítása nem sikerült" + +#: lib/passport.js:80 +msgid "Logged in as %s" +msgstr "Bejelenetezve, mint %s" + +#: lib/passport.js:180 +msgid "Incorrect username or password" +msgstr "Helytelen felhasználónév vagy jelszó" + +#: lib/subscription-mail-helpers.js:28 +msgid "%s: Subscription Confirmed" +msgstr "%s: Feliratkozás megerősítve" + +#: lib/subscription-mail-helpers.js:39 +msgid "%s: Email Address Already Registered" +msgstr "%s: Emailcím már regisztrálva van" + +#: lib/subscription-mail-helpers.js:49 +msgid "%s: Please Confirm Email Change in Subscription" +msgstr "%s: Kérjük erősítse meg email címének megváltoztatását" + +#: lib/subscription-mail-helpers.js:59 +msgid "%s: Please Confirm Subscription" +msgstr "%s: Kérjük erősítse meg a feliratkozást" + +#: lib/subscription-mail-helpers.js:69 +msgid "%s: Please Confirm Unsubscription" +msgstr "%s: Kérjük erősítse meg a leiratkozást" + +#: lib/subscription-mail-helpers.js:76 +msgid "%s: Unsubscription Confirmed" +msgstr "%s: Leiratkozás megerősítve" + +#: lib/tools.js:154 +msgid "Blocked email address \"%s\"" +msgstr "Blokkolt email cím \"%s\"" + +#: lib/tools.js:163 +msgid "Invalid email address \"%s\"." +msgstr "Érvénytelen email cím \"%s\"" + +#: lib/tools.js:166 +msgid "MX record not found for domain" +msgstr "MX rekord nem található a domain-hez" + +#: lib/tools.js:169 +msgid "Address domain not found" +msgstr "" + +#: lib/tools.js:172 +msgid "Address domain name is required" +msgstr "" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:230 +msgid "Not Found" +msgstr "Nem található" + +#: routes/archive.js:121 services/sender.js:454 +msgid "Received status code %s from %s" +msgstr "" + +#: routes/archive.js:153 routes/campaigns.js:894 +msgid "Attachment not found" +msgstr "Csatolmány nem található" + +#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:32 +#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:15 +#: 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 "" + +#: routes/campaigns.js:117 +msgid "Could not create campaign" +msgstr "" + +#: routes/campaigns.js:120 +msgid "Campaign “%s” created" +msgstr "" + +#: routes/campaigns.js:209 +msgid "Campaign settings updated" +msgstr "" + +#: routes/campaigns.js:211 +msgid "Campaign settings not updated" +msgstr "" + +#: routes/campaigns.js:227 routes/campaigns.js:744 +msgid "Campaign deleted" +msgstr "Kampány törölve" + +#: routes/campaigns.js:229 routes/campaigns.js:746 +msgid "Could not delete specified campaign" +msgstr "Kampány törlése nem sikerült" + +#: routes/campaigns.js:248 +msgid "Idling" +msgstr "" + +#: routes/campaigns.js:251 +msgid "Scheduled" +msgstr "Időzítve" + +#: routes/campaigns.js:257 +msgid "Paused" +msgstr "" + +#: routes/campaigns.js:259 +msgid "Inactive" +msgstr "Inaktív" + +#: routes/campaigns.js:261 +msgid "Active" +msgstr "Aktív" + +#: routes/campaigns.js:263 +msgid "Other" +msgstr "" + +#: routes/campaigns.js:421 +msgid "Unknown status selector" +msgstr "" + +#: routes/campaigns.js:762 +msgid "Scheduled sending" +msgstr "" + +#: routes/campaigns.js:764 +msgid "Could not schedule sending" +msgstr "" + +#: routes/campaigns.js:776 +msgid "Sending resumed" +msgstr "" + +#: routes/campaigns.js:778 +msgid "Could not resume sending" +msgstr "" + +#: routes/campaigns.js:790 +msgid "Sending reset" +msgstr "" + +#: routes/campaigns.js:792 +msgid "Could not reset sending" +msgstr "" + +#: routes/campaigns.js:804 routes/campaigns.js:832 +msgid "Sending paused" +msgstr "" + +#: routes/campaigns.js:806 routes/campaigns.js:834 +msgid "Could not pause sending" +msgstr "" + +#: routes/campaigns.js:818 +msgid "Sending activated" +msgstr "" + +#: routes/campaigns.js:820 +msgid "Could not activate sending" +msgstr "" + +#: routes/campaigns.js:855 +msgid "Attachment uploaded" +msgstr "" + +#: routes/campaigns.js:857 +msgid "Could not store attachment" +msgstr "" + +#: routes/campaigns.js:874 +msgid "Attachment deleted" +msgstr "" + +#: routes/campaigns.js:876 +msgid "Could not delete attachment" +msgstr "" + +#: routes/editorapi.js:38 +msgid "Invalid editor name" +msgstr "" + +#: routes/editorapi.js:146 +msgid "Method not supported" +msgstr "" + +#: routes/editorapi.js:381 +msgid "Invalid resource type or ID" +msgstr "" + +#: 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 "" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "" + +#: routes/forms.js:78 +msgid "Could not create custom form" +msgstr "" + +#: routes/forms.js:105 +msgid "Selected form not found" +msgstr "" + +#: routes/forms.js:136 +msgid "The plaintext version for this email" +msgstr "" + +#: routes/forms.js:137 +msgid "Custom forms use MJML for formatting" +msgstr "" + +#: routes/forms.js:138 +msgid "See the MJML documentation here" +msgstr "" + +#: routes/forms.js:146 +msgid "Layout" +msgstr "" + +#: routes/forms.js:152 +msgid "Form Input Style" +msgstr "" + +#: routes/forms.js:154 +msgid "" +"This CSS stylesheet defines the appearance of form input elements and alerts" +msgstr "" + +#: routes/forms.js:160 +msgid "Web - Subscribe" +msgstr "" + +#: routes/forms.js:165 +msgid "Web - Confirm Subscription Notice" +msgstr "" + +#: routes/forms.js:170 +msgid "Mail - Confirm Subscription (MJML)" +msgstr "" + +#: routes/forms.js:175 +msgid "Mail - Confirm Subscription (Text)" +msgstr "" + +#: routes/forms.js:180 +msgid "Mail - Already Subscribed (MJML)" +msgstr "" + +#: routes/forms.js:185 +msgid "Mail - Already Subscribed (Text)" +msgstr "" + +#: routes/forms.js:190 +msgid "Web - Subscribed Notice" +msgstr "" + +#: routes/forms.js:195 +msgid "Mail - Subscription Confirmed (MJML)" +msgstr "" + +#: routes/forms.js:200 +msgid "Mail - Subscription Confirmed (Text)" +msgstr "" + +#: routes/forms.js:208 +msgid "Web - Manage Preferences" +msgstr "" + +#: routes/forms.js:213 +msgid "Web - Manage Address" +msgstr "" + +#: routes/forms.js:218 +msgid "Web - Updated Notice" +msgstr "" + +#: routes/forms.js:226 +msgid "Web - Unsubscribe" +msgstr "" + +#: routes/forms.js:231 +msgid "Web - Confirm Unsubscription Notice" +msgstr "" + +#: routes/forms.js:236 +msgid "Mail - Confirm Unsubscription (MJML)" +msgstr "" + +#: routes/forms.js:241 +msgid "Mail - Confirm Unsubscription (Text)" +msgstr "" + +#: routes/forms.js:246 +msgid "Mail - Confirm Address Change (MJML)" +msgstr "" + +#: routes/forms.js:251 +msgid "Mail - Confirm Address Change (Text)" +msgstr "" + +#: routes/forms.js:256 +msgid "Web - Unsubscribed Notice" +msgstr "" + +#: routes/forms.js:261 +msgid "Mail - Unsubscription Confirmed (MJML)" +msgstr "" + +#: routes/forms.js:266 +msgid "Mail - Unsubscription Confirmed (Text)" +msgstr "" + +#: routes/forms.js:271 +msgid "Web - Manual Unsubscribe Notice" +msgstr "" + +#: routes/forms.js:309 +msgid "Form settings updated" +msgstr "" + +#: routes/forms.js:311 +msgid "Form settings not updated" +msgstr "" + +#: routes/forms.js:327 +msgid "Custom form deleted" +msgstr "" + +#: routes/forms.js:329 +msgid "Could not delete specified form" +msgstr "" + +#: routes/index.js:11 +msgid "Self Hosted Newsletter App" +msgstr "" + +#: routes/links.js:39 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "" + +#: routes/lists.js:82 +msgid "Could not create list" +msgstr "" + +#: routes/lists.js:85 +msgid "List created" +msgstr "Lista létrehozva" + +#: 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:657 routes/lists.js:714 routes/lists.js:741 +msgid "Could not find list with specified ID" +msgstr "" + +#: routes/lists.js:122 +msgid "List settings updated" +msgstr "" + +#: routes/lists.js:124 +msgid "List settings not updated" +msgstr "" + +#: routes/lists.js:142 +msgid "List deleted" +msgstr "Lista törölve" + +#: routes/lists.js:144 +msgid "Could not delete specified list" +msgstr "" + +#: routes/lists.js:206 +msgid "Unknown" +msgstr "" + +#: routes/lists.js:206 +msgid "Complained" +msgstr "" + +#: routes/lists.js:237 +msgid "Invalid key" +msgstr "" + +#: routes/lists.js:239 +msgid "Expired key" +msgstr "" + +#: routes/lists.js:241 +msgid "Revoked key" +msgstr "" + +#: routes/lists.js:288 +msgid "Force Subscribe" +msgstr "" + +#: routes/lists.js:291 +msgid "Initializing" +msgstr "" + +#: routes/lists.js:294 +msgid "Initialized" +msgstr "" + +#: routes/lists.js:297 +msgid "Importing" +msgstr "" + +#: routes/lists.js:303 +msgid "Errored" +msgstr "" + +#: routes/lists.js:381 routes/lists.js:450 routes/lists.js:475 +msgid "Could not find subscriber with specified ID" +msgstr "" + +#: routes/lists.js:427 +msgid "Could not add subscription" +msgstr "" + +#: routes/lists.js:432 +msgid "%s was successfully added to your list" +msgstr "" + +#: routes/lists.js:434 +msgid "%s was not added to your list" +msgstr "" + +#: routes/lists.js:456 +msgid "Could not unsubscribe user" +msgstr "" + +#: routes/lists.js:459 +msgid "%s was successfully unsubscribed from your list" +msgstr "" + +#: routes/lists.js:479 +msgid "%s was successfully removed from your list" +msgstr "" + +#: routes/lists.js:491 +msgid "Another subscriber with email address %s already exists" +msgstr "" + +#: routes/lists.js:498 +msgid "Subscription settings updated" +msgstr "" + +#: routes/lists.js:500 +msgid "Subscription settings not updated" +msgstr "" + +#: routes/lists.js:542 routes/lists.js:663 routes/lists.js:699 +#: routes/lists.js:727 routes/lists.js:747 +msgid "Could not find import data with specified ID" +msgstr "" + +#: routes/lists.js:573 +msgid "Could not process CSV" +msgstr "" + +#: routes/lists.js:589 +msgid "Could not create importer" +msgstr "" + +#: routes/lists.js:643 +msgid "Empty file" +msgstr "" + +#: routes/lists.js:646 +msgid "Too few rows" +msgstr "" + +#: routes/lists.js:703 +msgid "Import started" +msgstr "" + +#: routes/lists.js:731 +msgid "Import restarted" +msgstr "" + +#: routes/lists.js:797 +msgid "One-step (i.e. no email with confirmation link)" +msgstr "" + +#: routes/lists.js:803 +msgid "" +"One-step with unsubscription form (i.e. no email with confirmation link)" +msgstr "" + +#: routes/lists.js:809 +msgid "Two-step (i.e. an email with confirmation link will be sent)" +msgstr "" + +#: routes/lists.js:815 +msgid "" +"Two-step with unsubscription form (i.e. an email with confirmation link will " +"be sent)" +msgstr "" + +#: routes/lists.js:821 +msgid "" +"Manual (i.e. unsubscription has to be performed by the list administrator)" +msgstr "" + +#: routes/report-templates.js:246 +msgid "Could not create report template" +msgstr "" + +#: routes/report-templates.js:249 +msgid "Report template “%s” created" +msgstr "" + +#: routes/report-templates.js:257 +msgid "Could not find report template with specified ID" +msgstr "" + +#: routes/report-templates.js:280 +msgid "Report template updated" +msgstr "" + +#: routes/report-templates.js:282 +msgid "Report template not updated" +msgstr "" + +#: routes/report-templates.js:298 +msgid "Report template deleted" +msgstr "" + +#: routes/report-templates.js:300 +msgid "Could not delete specified report template" +msgstr "" + +#: routes/reports.js:124 routes/reports.js:130 +msgid "Could not create report" +msgstr "" + +#: routes/reports.js:135 +msgid "Report “%s” created" +msgstr "" + +#: 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 "" + +#: routes/reports.js:188 routes/reports.js:194 +msgid "Could not update report" +msgstr "" + +#: routes/reports.js:197 +msgid "Report updated" +msgstr "" + +#: routes/reports.js:199 +msgid "Report not updated" +msgstr "" + +#: routes/reports.js:212 +msgid "Report deleted" +msgstr "" + +#: routes/reports.js:214 +msgid "Could not delete specified report" +msgstr "" + +#: routes/reports.js:230 +msgid "Could not find report template" +msgstr "" + +#: routes/reports.js:260 +msgid "Unknown type of template" +msgstr "" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "" + +#: 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 "" + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "" + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "" + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "" + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "" + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "" + +#: routes/subscription.js:33 +msgid "Not allowed by CORS" +msgstr "" + +#: routes/subscription.js:61 routes/subscription.js:176 +#: routes/subscription.js:286 routes/subscription.js:382 +#: routes/subscription.js:459 routes/subscription.js:535 +#: routes/subscription.js:566 routes/subscription.js:626 +#: routes/subscription.js:682 routes/subscription.js:760 +#: routes/subscription.js:897 +msgid "Selected list not found" +msgstr "Lista nem található" + +#: routes/subscription.js:93 +msgid "Could not save subscription" +msgstr "Feliratkozás mentése nem sikerült" + +#: routes/subscription.js:118 +msgid "Subscriber info corrupted or missing" +msgstr "" + +#: routes/subscription.js:136 +msgid "Email address changed" +msgstr "Email cím megváltoztatva." + +#: routes/subscription.js:179 routes/subscription.js:385 +msgid "The list does not allow public subscriptions." +msgstr "A lista nem fogad nyilvános feliratkozásokat." + +#: routes/subscription.js:355 routes/subscription.js:357 +msgid "Email address not set" +msgstr "Email cím nincs megadva." + +#: routes/subscription.js:653 +msgid "" +"An email with further instructions has been sent to the provided address" +msgstr "A további teendőket tartalmazó levelet kiküldtük a megadott címre. " + +#: routes/subscription.js:861 routes/subscription.js:877 +msgid "Public key is not set" +msgstr "Publikus kulcs nincs megadva." + +#: routes/templates.js:84 +msgid "Could not create template" +msgstr "Sablon létrehozása nem sikerült" + +#: routes/templates.js:87 +msgid "Template created" +msgstr "Sablon létrehozva." + +#: routes/templates.js:126 +msgid "Template settings updated" +msgstr "Sablon beállításai frissítve" + +#: routes/templates.js:128 +msgid "Template settings not updated" +msgstr "" + +#: routes/templates.js:144 +msgid "Template duplicated" +msgstr "" + +#: routes/templates.js:146 +msgid "Could not duplicate specified template" +msgstr "" + +#: routes/templates.js:157 +msgid "Template deleted" +msgstr "Sablon törölve." + +#: routes/templates.js:159 +msgid "Could not delete specified template" +msgstr "" + +#: routes/triggers.js:62 routes/triggers.js:79 routes/triggers.js:154 +msgid "Could not find selected list" +msgstr "" + +#: routes/triggers.js:131 +msgid "Could not create trigger" +msgstr "" + +#: routes/triggers.js:138 +msgid "Trigger “%s” created" +msgstr "" + +#: routes/triggers.js:214 +msgid "Trigger settings updated" +msgstr "" + +#: routes/triggers.js:216 +msgid "Trigger settings not updated" +msgstr "" + +#: routes/triggers.js:228 +msgid "Trigger deleted" +msgstr "" + +#: routes/triggers.js:230 +msgid "Could not delete specified trigger" +msgstr "" + +#: routes/triggers.js:242 +msgid "Could not find trigger with specified ID" +msgstr "" + +#: routes/triggers.js:255 +msgid "Trigger not found" +msgstr "" + +#: 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 "" + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "A jelszava sikeresen megváltoztatva." + +#: routes/users.js:87 +msgid "User data not found" +msgstr "" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "" + +#: services/feedcheck.js:146 +msgid "RSS entry %s" +msgstr "RSS bejegyzés %s" + +#: services/importer.js:249 +msgid "Could not access import file" +msgstr "Import file nem érhető el" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "" diff --git a/languages/it_IT.mo b/languages/it_IT.mo new file mode 100644 index 00000000..7e5a8f97 Binary files /dev/null and b/languages/it_IT.mo differ diff --git a/languages/it_IT.po b/languages/it_IT.po new file mode 100644 index 00000000..b35ef79f --- /dev/null +++ b/languages/it_IT.po @@ -0,0 +1,4661 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Language-Team: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2018-04-11 12:42+0200\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2018-04-05 15:43+0200\n" +"X-Generator: Poedit 1.8.7.1\n" +"Last-Translator: \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Language: it\n" + +#: views/archive/layout.hbs:1 views/layout.hbs:1 +msgid "Self hosted email newsletter app" +msgstr "Applicazione di newsletter self-hosted" + +#: 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 "Home" + +#: views/blacklist.hbs:2 views/blacklist.hbs:3 views/layout.hbs:7 +#: views/lists/subscription/edit.hbs:15 +msgid "Blacklist" +msgstr "Lista nera" + +#: views/blacklist.hbs:4 views/users/api.hbs:76 +msgid "Add email to blacklist" +msgstr "Aggiungi email alla lista nera" + +#: views/blacklist.hbs:5 +msgid "Add" +msgstr "Aggiungi" + +#: views/blacklist.hbs:6 +msgid "Email" +msgstr "Email" + +#: 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 "Campagne" + +#: views/campaigns/blacklisted.hbs:3 views/campaigns/blacklisted.hbs:4 +msgid "Blacklisted info" +msgstr "Informazioni lista nera" + +#: 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 "Vedi campagna" + +#: views/campaigns/blacklisted.hbs:6 +msgid "Subscribers who blacklisted by global blacklist:" +msgstr "Iscritti in lista nera derivati dalla lista nera globale:" + +#: 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 "Indirizzo" + +#: 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 "Nome" + +#: 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 "Cognome" + +#: views/campaigns/blacklisted.hbs:10 +msgid "Reason" +msgstr "Ragione" + +#: views/campaigns/blacklisted.hbs:11 +msgid "Time" +msgstr "Tempo" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Informazioni rimbalzo" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Iscritti che hanno rimbalzato e sono stati rimossi:" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "Risposta SMTP" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Tempo di rimbalzo" + +#: 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 "Crea Campagna" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Campagna regolare" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "Campagna RSS" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "Campagna attivabile" + +#: 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 "Nome" + +#: 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 "Descrizione" + +#: 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 "Stato" + +#: 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 "Creato" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Informazioni link" + +#: 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 "% di clicks" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:66 +msgid "% of messages" +msgstr "% di messagi" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:69 +msgid "Aggregated clicks" +msgstr "Clicks aggregati" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Iscritti che hanno cliccato un link:" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Iscritti che hanno cliccato questo link:" + +#: views/campaigns/clicked.hbs:13 views/campaigns/opened.hbs:7 +msgid "Stats by country" +msgstr "Statistiche per nazione" + +#: views/campaigns/clicked.hbs:14 views/campaigns/opened.hbs:8 +msgid "Stats by device type" +msgstr "Statistiche per tipo di dispositivo" + +#: views/campaigns/clicked.hbs:18 +msgid "First click time" +msgstr "Tempo primo click" + +#: views/campaigns/clicked.hbs:19 +msgid "Click count" +msgstr "Conteggio clicks" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Informazioni reclamo" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Iscritti che hanno reclamato e sono stati rimossi" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Tempo reclamo" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:21 +msgid "Create RSS Campaign" +msgstr "Crea campagna 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 "" +"Le campagne RSS configurano un registratore per gli indirizzi dei feed RSS. " +"Qualvolta un nuovo elemento venga trovato per il feed, viene inviato alle " +"liste selezionate come una email." + +#: 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 "Nome campagna" + +#: 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 "HTML è consentito" + +#: 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 "Seleziona" + +#: 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 "Iscritti" + +#: views/campaigns/create-rss.hbs:13 views/campaigns/edit-rss.hbs:15 +msgid "RSS Feed Url" +msgstr "URL del feed RSS" + +#: 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 "" +"I nuovi elementi per questo feed RSS sono inviati agli iscritti come " +"messaggi email." + +#: 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 da \"Nome\"" + +#: 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 "Questo è il nome che verrà' visualizzato come mittente" + +#: 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 "Email dall'indirizzo" + +#: 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 "Questo è l'indirizzo a cui gli iscritti risponderanno" + +#: 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 "Disabilita tracciamento aperture" + +#: 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 "Disabilita tracciamento clicks" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "Crea una campagna attivabile" + +#: 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 "Template" + +#: views/campaigns/create-triggered.hbs:13 views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "Seleziona un template:" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "Selezionando un template, si crea una copia su cui andare a lavorare" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "In alternativa utilizza un URL per il contenuto del messaggio:" + +#: 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 "" +"Se un messaggio viene inviato questa URL verrà' richiamata assorbendo i " +"merge tags come body del messaggio. Utilizza questa opzione se vuoi scrivere " +"tu l'HTML del messaggio" + +#: 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 "Oggetto dell'email" + +#: 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 "Ricordati di mantenerla rilevante e non creare 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 "" +"Questo è l'indirizzo a cui gli iscritti risponderanno se non specifichi un " +"indirizzo email 'reply-to'" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "Indirizzo email 'reply-to'" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "Se impostato, questo sarà' l'indirizzo a cui le persone risponderanno" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "Informazioni della consegna" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" +"Iscritti che hanno ricevuto il messaggio e non hanno reclamato e non si sono " +"rimossi" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "Ora di consegna" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "Modifica campagna 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 "Impostazioni generali" + +#: 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 "" +"Utilizza tag di assorbimento speciali [RSS_ENTRY] per evidenziare il " +"contenuto del messaggio all'interno del RSS. Puoi inoltre utilizzare " +"qualsiasi tag di merge valido." + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:35 +msgid "Delete Campaign" +msgstr "Cancella " + +#: 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:97 views/templates/edit.hbs:13 +#: views/triggers/edit.hbs:30 views/users/account.hbs:18 +msgid "Update" +msgstr "Aggiorna" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "Modifica campagna azionabile" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +#: routes/forms.js:143 +msgid "General" +msgstr "Generale" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:27 +msgid "Template Settings" +msgstr "Impostazioni Template" + +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:28 +msgid "Template URL" +msgstr "URL Template" + +#: 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 "Modifica Campagna" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:30 +msgid "Attachments" +msgstr "Allegati" + +#: views/campaigns/edit.hbs:31 +msgid "File" +msgstr "File" + +#: views/campaigns/edit.hbs:32 +msgid "Size" +msgstr "Dimensione" + +#: 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 "Non ci sono dati disponibili nella tabella" + +#: views/campaigns/edit.hbs:34 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "Aggiungi allegato" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "Informazioni apertura" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "Iscritti che hanno aperto il messaggio:" + +#: views/campaigns/opened.hbs:12 +msgid "First open" +msgstr "Prima apertura" + +#: views/campaigns/opened.hbs:13 +msgid "Opened count" +msgstr "Numero di aperture" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "Informazioni utenti rimossi" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "Iscritti che si sono rimossi:" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:28 +#: views/lists/subscription/import.hbs:13 routes/lists.js:206 +msgid "Unsubscribed" +msgstr "Rimossi" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "Carica" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "Panoramica" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "Links" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "Feed URL" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "Ultimo controllo" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "Non ancora controllati" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "attiva la campagna per controllare il feed per nuovi messaggi" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "Stato RSS" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "Guarda l'anteprima come" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "Aggiungi un utente di test" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "Non sono presenti utenti di test, creane uno qui" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "Vai" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:26 +msgid "Delivered" +msgstr "Consegnato" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "Elenca iscritti che hanno ricevuto il messaggio" + +#: views/campaigns/view.hbs:22 +msgid "Blacklisted" +msgstr "In lista nera" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who blacklisted by global blacklist" +msgstr "Lista degli iscritti in lista nera per la lista nera globale" + +#: views/campaigns/view.hbs:24 routes/lists.js:206 +msgid "Bounced" +msgstr "Rimbalzato" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who bounced" +msgstr "Lista utenti che hanno rimbalzato" + +#: views/campaigns/view.hbs:26 +msgid "Complaints" +msgstr "Reclami" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who complained for this message" +msgstr "Lista degli iscritti che hanno fatto un reclamo per questo messaggio" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who unsubscribed after this message" +msgstr "" +"Lista degli utenti che si sono cancellati in seguito a questo messaggio" + +#: views/campaigns/view.hbs:30 +msgid "Opened" +msgstr "Aperta" + +#: views/campaigns/view.hbs:31 +msgid "List subscribers who opened this message" +msgstr "Lista degli iscritti che hanno aperto questo messaggio" + +#: views/campaigns/view.hbs:32 +msgid "Clicked" +msgstr "Cliccate" + +#: views/campaigns/view.hbs:33 views/campaigns/view.hbs:70 +msgid "List subscribers who clicked on a link" +msgstr "Lista degli iscritti che hanno cliccato su di un link" + +#: views/campaigns/view.hbs:34 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" +"Sei sicuro? Questa azione farà iniziare l'invio dei messaggi alla lista " +"selezionata" + +#: views/campaigns/view.hbs:35 +msgid "Delay sending" +msgstr "Ritarda invio" + +#: views/campaigns/view.hbs:36 +msgid "hours" +msgstr "ore" + +#: views/campaigns/view.hbs:37 +msgid "minutes" +msgstr "minuti" + +#: views/campaigns/view.hbs:38 +msgid "Send to subscribers:" +msgstr "Invia agli iscritti:" + +#: views/campaigns/view.hbs:39 +msgid "Are you sure? This action would reset scheduling" +msgstr "Sei sicuro? Questa azione resetterà la programmazione" + +#: views/campaigns/view.hbs:40 +msgid "Cancel" +msgstr "Annulla" + +#: views/campaigns/view.hbs:41 +msgid "Sending scheduled" +msgstr "Invio programmato" + +#: 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 "Invio in corso" + +#: 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 "" +"Sei sicuro? Questa azione riprenderà l'invio dei messaggi alla lista " +"selzionata" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" +"Sei sicuro? Questa azione resetterà tutte le statistiche riguardanti il " +"progresso attuale" + +#: views/campaigns/view.hbs:46 +msgid "Resume" +msgstr "Riprendi" + +#: views/campaigns/view.hbs:47 views/campaigns/view.hbs:51 +msgid "Reset" +msgstr "Resetta" + +#: views/campaigns/view.hbs:50 +msgid "Continue" +msgstr "Continua" + +#: views/campaigns/view.hbs:52 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" +"Tutti i messaggi inviati! Premi \"Continua\" se vuoi inviare questa campagna " +"ai nuovi iscritti" + +#: 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 "" +"Sei sicuro? Questa azione metterà in pausa l'invio dei nuovi ingressi dal " +"feed RSS come email alla lista selezionata" + +#: views/campaigns/view.hbs:55 views/campaigns/view.hbs:59 +msgid "Campaign status:" +msgstr "Stato campagna:" + +#: views/campaigns/view.hbs:56 +msgid "ACTIVE" +msgstr "ATTIVA" + +#: 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 "" +"Sei sicuro? Questa azione iniziera ad inviare i nuovi ingressi dal feed RSS " +"come email alla lista selezionata" + +#: views/campaigns/view.hbs:58 +msgid "Activate" +msgstr "Attiva" + +#: views/campaigns/view.hbs:60 +msgid "INACTIVE" +msgstr "INATTIVA" + +#: 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 "" +"Questa e' una campagna innescabile. I messaggi sono inviati solo agli " +"iscritti che hanno innescato questa campagna" + +#: views/campaigns/view.hbs:62 +msgid "see more" +msgstr "guarda di più" + +#: views/campaigns/view.hbs:67 +msgid "List subscribers who clicked this link" +msgstr "Lista iscritti che hanno cliccato questo 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 "" +"I clicks sono contati come iscritti unici che hanno cliccato un link " +"specifico o (nella vista aggregata) su di un qualsiasi link" + +#: 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 "" +"Se un nuovo ingresso è trovato nel feed della campagna, una nuova sotto-" +"campagna verrà creata per quella entry e sarà visualizzata ui" + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "Cambia la tua password" + +#: 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 "" +"Abbiamo ricevuto una richiesta di cambio password per il tuo account " +"Mailtrain" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "Cambia password" + +#: 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 "" +"Se non hai chiesto tu di cambiare password, puoi ignorare questa email e la " +"tua password resterà la stessa." + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "Preferenze" + +#: 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 "Cancella iscrizione" + +#: 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 "Guarda questa email nel tuo browser" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "Ciao [FIRST_NAME/Customer]," + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "Ringraziandoti" + +#: views/index.hbs:1 +msgid "List Management" +msgstr "Gestione 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 ti permette di gestire facilmente grosse liste. Milioni di " +"iscritti? Nessun problema. Puoi aggiungere iscritti manualmente, via API " +"oppure importarli tramite CSV. Tutte le liste hanno la possibilità di " +"aggiungere campi personalizzati e tag di merge." + +#: 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 "Guarda di più" + +#: 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 "Campi personalizzati" + +#: 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 "" +"Campi di testo, numeri, drop downs o segni di spunta, Mailtrain li " +"implementa tutti. Ogni campo personalizzato può essere incluso nella " +"newsletter generata tramite i tag di merge." + +#: views/index.hbs:6 +msgid "Mailtrain also supports custom forms." +msgstr "Mailtrain supporta form personalizzati" + +#: views/index.hbs:8 +msgid "List Segmentation" +msgstr "Segmentazione della lista" + +#: 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 "" +"Invia messaggi solo agli iscritti che rientrano in una regola di " +"segmentazione predefinita. Non c'è bisogno di creare liste separate con " +"piccole differenze." + +#: views/index.hbs:11 +msgid "RSS Campaigns" +msgstr "Campagne 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 per tracciare i feed RSS e se viene identificato un " +"nuovo ingresso Mailtrain auto genera una nuova campagna utilizzando i dati " +"del contenuto del messaggio del feed e lo invia agli iscritti selezionati." + +#: views/index.hbs:14 +msgid "GPG Encryption" +msgstr "Crittografia 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 "" +"Se una lista ha un campo personalizzato per un set di chiave pubblica GPG " +"allora gli iscritti possono caricare la loro chiave GPG pubblica per " +"ricevere messaggi criptati dalla lista." + +#: views/index.hbs:17 +msgid "Click Stats" +msgstr "Statistiche click" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" +"Dopo che una campagna è stata inviata, controlla le statistiche di ogni " +"click per ogni link incluso nel messaggio." + +#: views/index.hbs:20 +msgid "Template Editors" +msgstr "Editors dei templates" + +#: 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 include GrapeJS e Mosaico, due editors di template avanzati. " +"Mailtrain offre inoltre un editor di codice se preferisci scrivere tu l'HTML." + +#: views/index.hbs:23 +msgid "Send via Any Provider" +msgstr "Invia tramite qualsiasi provider" + +#: views/index.hbs:24 +msgid "" +"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 "" +"Puoi utilizzare qualsiasi provider che supporti SMTP per inviare la tua " +"newsletter. La gestione di rimbalzi e reclami via webhooks è supportata per " +"SES, SparkPost, SendGrid, Mailgun, Postfix e ZoneMTA." + +#: views/index.hbs:26 lib/tools.js:137 +msgid "Automation" +msgstr "Automazione" + +#: views/index.hbs:27 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" +"Definisci gli inneschi di automazione per l'invio di messaggi specifici " +"quando un utente innesca una azione specifica." + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "Attiva navigazione" + +#: 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 "Account" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "Impostazioni" + +#: 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 "Esci" + +#: 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 "Log in" + +#: views/layout.hbs:11 +msgid "Self Hosted Newsletter App Built on Top of Nodemailer" +msgstr "" +"Applicazione per newsletter self hosted costruita sulla base di Nodemailer" + +#: views/layout.hbs:12 views/layout.hbs:14 +msgid "Source on GitHub" +msgstr "Sorgenti su GitHub" + +#: views/layout.hbs:13 +msgid "Subscribe to Our Newsletter" +msgstr "Iscriviti alla nostra Newsletter" + +#: 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 "Liste" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:13 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "Crea Lista" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "Nome lista" + +#: views/lists/create.hbs:9 views/lists/edit.hbs:15 +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "Iscrizione" + +#: views/lists/create.hbs:10 views/lists/edit.hbs:16 +msgid "Allow public users to subscribe themselves" +msgstr "Permetti utenti pubblici di iscriversi da soli" + +#: views/lists/create.hbs:11 views/lists/edit.hbs:17 +msgid "Unsubscription" +msgstr "Cancellazione iscrizione" + +#: views/lists/create.hbs:12 views/lists/edit.hbs:18 +msgid "Select how an unsuscription request by subscriber is handled." +msgstr "" +"Seleziona come una richiesta di rimozione dell'iscrizione viene gestita." + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:8 +msgid "Edit List" +msgstr "Modifica lista" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "Guarda Lista" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "ID lista" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "Questo è l'ID della lista mostrato agli iscritti" + +#: views/lists/edit.hbs:12 +msgid "Custom Form" +msgstr "Form personalizzato" + +#: views/lists/edit.hbs:13 views/lists/forms/forms.hbs:11 +msgid "Default Mailtrain Form" +msgstr "Form predefinito di Mailtrain" + +#: views/lists/edit.hbs:14 +msgid "" +"The custom form used for this list. You can create a form here." +msgstr "" +"Il form personalizzato per questa lista. Puoi creare un form qui." + +#: views/lists/edit.hbs:19 +msgid "Delete List" +msgstr "Cancella lista" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "Crea campo" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "Crea campo personalizzato" + +#: 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 "Nome campo" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "Tipo campo" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "Testo" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "Numero" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "Sito web" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "Chiave pubblica GPG" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "Testo multi linea" + +#: 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 "Data" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "Data (MM/GG/ANNO)" + +#: views/lists/fields/create.hbs:17 views/lists/fields/edit.hbs:18 +#: lib/models/fields.js:26 +msgid "Date (DD/MM/YYYY)" +msgstr "Data (GG/MM/ANNO)" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "Compleanno" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "Compleanno (MM/GG)" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "Compleanno (GG/MM)" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "Raggruppati" + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "Drop Downs" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "Radio Buttons" + +#: 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 "Option for group value" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "Group" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "Richiesto per le group options" + +#: 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 "Valore di merge tag di default" + +#: 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 "" +"Per i gruppi di elementi come i checkboxes, puoi controllare l'aspetto " +"visivo del merge tag con un template opzionale. Il template usa la sintassi " +"di handlebars e puoi trovare tutti i valori dall'array {{values}}, per esempio {{#each values}} {{this}} {{/each}}. Se il " +"template non è definito allora i valori multipli vengono uniti dalla " +"virgola. Puoi anche utilizzare questo template per visualizzare valori JSON " +"(se il valore JSON è un array allora l'array viene esposto come " +"values, altrimenti puoi accedere direttamente alle keys del " +"JSON)." + +#: views/lists/fields/create.hbs:33 views/lists/fields/edit.hbs:37 +msgid "Visible" +msgstr "Visibile" + +#: views/lists/fields/create.hbs:34 +msgid "Add Field" +msgstr "Aggiungi campo" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "Modifica campo" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "Modifica campo personalizzato" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "Indietro ai campi" + +#: 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 "Merge tag" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "Merge Tag" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "Metti questo tag nel tuo contenuto:" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "Elimina 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:183 routes/triggers.js:297 +msgid "Edit" +msgstr "Modifica" + +#: 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 "Form personalizzati" + +#: views/lists/forms/create.hbs:4 +msgid "Create Form" +msgstr "Crea form" + +#: views/lists/forms/create.hbs:5 views/lists/forms/forms.hbs:4 +msgid "Create Custom Form" +msgstr "Crea form personalizzato" + +#: 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 "Nome form" + +#: views/lists/forms/create.hbs:8 +msgid "Add Form" +msgstr "Aggiungi form" + +#: views/lists/forms/edit.hbs:4 +msgid "Edit Form" +msgstr "Modifica Form" + +#: views/lists/forms/edit.hbs:5 +msgid "Edit Custom Form" +msgstr "Modifica form personalizzato" + +#: views/lists/forms/edit.hbs:6 +msgid "Back to forms" +msgstr "Indietro ai forms" + +#: views/lists/forms/edit.hbs:10 +msgid "Optional comments about this form" +msgstr "Commenti opzionali per questo form" + +#: views/lists/forms/edit.hbs:11 +msgid "Form Preview" +msgstr "Anteprima Form" + +#: 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: Questi links sono solo per un'anteprima veloce. Se confermi l'invio di " +"un form in anteprima verrai rediretto al form di default della lista." + +#: 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 "Iscriviti" + +#: views/lists/forms/edit.hbs:14 +msgid "Confirm Subscription Notice" +msgstr "Notifica iscrizione confermata" + +#: views/lists/forms/edit.hbs:15 +msgid "Confirm Unsubscription Notice" +msgstr "Conferma notifica di cancellazione iscrizione" + +#: views/lists/forms/edit.hbs:16 +msgid "Subscribed Notice" +msgstr "Notifica iscrizione" + +#: views/lists/forms/edit.hbs:17 +msgid "Updated Notice" +msgstr "Notifica di aggiornamento" + +#: views/lists/forms/edit.hbs:18 +msgid "Unsubscribed Notice" +msgstr "Notifica di cancellazione di iscrizione" + +#: views/lists/forms/edit.hbs:19 +msgid "Manual Unsubscribe Notice" +msgstr "Notifica di cancellazione iscrizione manuale" + +#: views/lists/forms/edit.hbs:21 routes/forms.js:205 +msgid "Manage" +msgstr "Gestisci" + +#: views/lists/forms/edit.hbs:22 +msgid "Manage Address" +msgstr "Gestisci indirizzo" + +#: views/lists/forms/edit.hbs:23 +msgid "Create a test user for additional options" +msgstr "Crea un utente di test per le opzioni aggiuntive" + +#: 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 "Templates" + +#: views/lists/forms/edit.hbs:25 +msgid "Fields" +msgstr "Campi" + +#: views/lists/forms/edit.hbs:27 +msgid "Form Fields" +msgstr "Campi del form" + +#: views/lists/forms/edit.hbs:28 +msgid "Fields hidden on subscription page:" +msgstr "Campi nascosti sulla pagina di iscrizione:" + +#: views/lists/forms/edit.hbs:29 +msgid "Fields shown on subscription page:" +msgstr "Campi mostrati sulla pagina di iscrizione:" + +#: views/lists/forms/edit.hbs:30 +msgid "Fields hidden on preferences page:" +msgstr "Campi nascosti sulla pagina delle preferenze:" + +#: views/lists/forms/edit.hbs:31 +msgid "Fields shown on preferences page:" +msgstr "Campi mostrati sulla pagina delle preferenze:" + +#: views/lists/forms/edit.hbs:32 +msgid "Delete Form" +msgstr "Cancella form" + +#: views/lists/forms/forms.hbs:10 +msgid "The default form for this list is:" +msgstr "Il form di default per questa lista:" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "ID" + +#: views/lists/lists.hbs:7 views/reports/partials/report-fields.hbs:10 +msgid "Subscribers" +msgstr "Iscritti" + +#: 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 "Segmenti" + +#: 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 "Crea 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 "Nome segmento" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "Regola di corrispondenza" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "Tutte le regole devono corrispondere con" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "Qualsiasi regola può corrispondere con" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "Aggiungi 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 "Modifica segmento" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "Indietro ai segmenti" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "Elimina 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 "Crea regola" + +#: 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 "Regola" + +#: 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 "Valore" + +#: 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 "" +"Usa % come carattere jolly, per esempio \"%test\" fa passare tutti i valori " +"che finiscono con \"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 "Usa corrispondenza esatta" + +#: 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 "Usa range di corrispondenza" + +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-edit.hbs:20 +msgid "Use relative range match" +msgstr "Usa range di corrispondenza relativo" + +#: 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 "Da" + +#: 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 "giorni" + +#: 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 "prima di oggi" + +#: 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 "dopo di oggi" + +#: 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 "a" + +#: 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 "Selezionati" + +#: 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 "Non selezionati" + +#: views/lists/segments/rule-configure.hbs:33 +msgid "Add Rule" +msgstr "Aggiungi regola" + +#: views/lists/segments/rule-create.hbs:8 +#: views/lists/subscription/import.hbs:18 +#: views/reports/create-select-template.hbs:5 +#: views/triggers/create-select.hbs:9 +msgid "Next" +msgstr "Avanti" + +#: views/lists/segments/rule-edit.hbs:37 +msgid "Delete Rule" +msgstr "Cancella regola" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "Corrispondi" + +#: 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 "Corrisponde alla regola" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "Iscritti corrispondenti" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "mostra" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "Aggiungi iscritto" + +#: views/lists/subscription/add.hbs:5 +#: views/subscription/partials/subscription-custom-fields.hbs:1 +#: views/users/account.hbs:7 +msgid "Email Address" +msgstr "Indirizzo Email" + +#: views/lists/subscription/add.hbs:8 views/lists/subscription/edit.hbs:9 +#: views/settings.hbs:80 views/settings.hbs:95 +#: views/subscription/partials/subscription-custom-fields.hbs:6 +msgid "Begins with" +msgstr "Inizia con" + +#: 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 "" +"Inserisci chiave pubblica GPG che verrà usata per crittografare i messaggi " +"per questo iscritto" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "Fuso orario" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "Utente di 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 "" +"Se selezionato, questa iscrizione può essere usata per l'anteprima dei " +"messaggi della campagna" + +#: 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 "" +"Questa persona non riceverà una email di conferma, quindi assicurati di " +"avere il permesso di inviargli emails." + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "Modifica iscrizione" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "Indietro alla 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 "Indirizzo email" + +#: views/lists/subscription/edit.hbs:17 +msgid "Delete Subscription" +msgstr "Elimina iscrizione" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "Stato dell' Import" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "Indiirizzi falliti" + +#: 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 "" +"Gli indirizzi di ruolo come postmaster@example.com sono bloccati durante " +"l'import. Questi indirizzi possono iscriversi alla tua lista con il form di " +"iscrizione online." + +#: views/lists/subscription/import-failed.hbs:7 +msgid "see here" +msgstr "guarda qui" + +#: views/lists/subscription/import-failed.hbs:9 +msgid "Fail reason" +msgstr "Motivo di errore" + +#: 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 "Importa iscritti" + +#: views/lists/subscription/import-preview.hbs:10 views/users/api.hbs:21 +#: views/users/api.hbs:36 views/users/api.hbs:44 views/users/api.hbs:52 +#: views/users/api.hbs:64 views/users/api.hbs:75 views/users/api.hbs:83 +#: views/users/api.hbs:91 views/users/api.hbs:96 +msgid "Example" +msgstr "Esempio" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "Inizia import" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "File CSV" + +#: views/lists/subscription/import.hbs:6 +msgid "The first line must contain column headings" +msgstr "La prima linea deve contenere le intestazioni di colonna" + +#: views/lists/subscription/import.hbs:7 +msgid "CSV delimiter" +msgstr "Delimitatore file CSV" + +#: views/lists/subscription/import.hbs:8 +msgid "Categorize the imported subscribers as" +msgstr "Categorizza gli iscritti importati come" + +#: views/lists/subscription/import.hbs:9 routes/lists.js:206 +msgid "Subscribed" +msgstr "Iscritto" + +#: views/lists/subscription/import.hbs:10 +msgid "Regular subscriber addresses" +msgstr "Indirizzi regolari degli iscritti" + +#: views/lists/subscription/import.hbs:11 +msgid "Subscribed (Force)" +msgstr "Iscrizione (Forzata)" + +#: views/lists/subscription/import.hbs:12 +msgid "Regular subscriber addresses, resubscribe users that have unsubscribed" +msgstr "" +"Indirizzi regolari degli utenti registrati, riscrizione utenti che hanno " +"cancellato l'iscrizione" + +#: views/lists/subscription/import.hbs:14 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "Email soppresse che saranno cancellate dalla tua lista" + +#: views/lists/subscription/import.hbs:15 +msgid "Check imported emails" +msgstr "Controlla le emails importate" + +#: views/lists/subscription/import.hbs:16 views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "Abilitato" + +#: views/lists/subscription/import.hbs:17 views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "Disabilitato" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "Form di iscrizione" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "Lista azioni" + +#: 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 "Crea innesco" + +#: views/lists/view.hbs:10 +msgid "Add Subscriber" +msgstr "Aggiungi iscrizione" + +#: views/lists/view.hbs:11 +msgid "Import Subscribers" +msgstr "Importa iscrizioni" + +#: views/lists/view.hbs:15 +msgid "Create New Segment" +msgstr "Crea nuovo segmento" + +#: views/lists/view.hbs:16 +msgid "Filter" +msgstr "Filtra" + +#: views/lists/view.hbs:17 +msgid "Subscriptions" +msgstr "Iscrizioni" + +#: views/lists/view.hbs:18 +msgid "Imports" +msgstr "Importazioni" + +#: views/lists/view.hbs:25 routes/campaigns.js:255 routes/lists.js:300 +msgid "Finished" +msgstr "Finito" + +#: views/lists/view.hbs:27 +msgid "Added" +msgstr "Aggiunto" + +#: views/lists/view.hbs:28 +msgid "Updated" +msgstr "Aggiornato" + +#: views/lists/view.hbs:29 +msgid "Failed" +msgstr "Errore" + +#: views/lists/view.hbs:31 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" +"Sei sicuro? Questa azione deve essere chiamata solo per risolvere " +"importazioni in stato di stallo" + +#: views/lists/view.hbs:32 +msgid "Restart" +msgstr "Riavvia" + +#: views/mosaico/editor.hbs:1 views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "Referenza dei tag di merge" + +#: views/mosaico/editor.hbs:4 +msgid "MOSAICO Responsive Email Designer" +msgstr "MOSAICO REsponsive Email Designer" + +#: views/mosaico/editor.hbs:5 +msgid "Sucessfully saved" +msgstr "Salvato con successo" + +#: views/mosaico/editor.hbs:6 +msgid "An error occured while saving the document" +msgstr "Errore durante il salvataggio del documento" + +#: views/mosaico/editor.hbs:7 +msgid "Unsaved changes will be lost. Close now?" +msgstr "Le modifiche non salvate andranno perdute, Chiudere ora?" + +#: views/mosaico/editor.hbs:8 views/mosaico/editor.hbs:9 +msgid "Tags" +msgstr "Tags" + +#: 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 "Contenuto template (HTML)" + +#: views/partials/editor-navbar.hbs:1 +msgid "SAVE" +msgstr "SALVA" + +#: views/partials/editor-navbar.hbs:2 +msgid "SAVING" +msgstr "STO SALVANDO" + +#: views/partials/editor-navbar.hbs:3 +msgid "CLOSE" +msgstr "CHIUDI" + +#: views/partials/grapejs.hbs:2 +msgid "Open GrapeJS" +msgstr "Apri GrapeJS" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "Attiva anteprima HTML" + +#: views/partials/html-to-text.hbs:1 +msgid "" +"To extract the text from HTML click here." +msgstr "" +"Per estrarre il testo dal codice HTML clicca qui." + +#: 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 "" +"Per favore nota che il tuo testo nel campo qui sopra verrà sovrascritto. " +"Questa caratteristica usa le Premailer API, un servizio di " +"terzi. I loro Termini per il servizio e la loro Privacy Policy è applicato." + +#: views/partials/html-to-text.hbs:3 +msgid "An error occurred while talking to the server" +msgstr "C'è stato un errore nel comunicare con il server" + +#: 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 sono tags che sono sostituiti prima dell'invio del messaggio. Il " +"formato del merge tag è il seguente [TAG_NAME] oppure " +"[TAG_NAME/fallback] dove fallback è un valore " +"opzionale di testo utilizzato quando TAG_NAME è vuoto." + +#: views/partials/modal-carousel.hbs:1 +msgid "{{title}}" +msgstr "{{}}" + +#: views/partials/mosaico.hbs:2 +msgid "Open Mosaico" +msgstr "Apri Mosaico" + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "Contenuto del template (test semplice)" + +#: 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 "Reports" + +#: 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 "Crea template" + +#: views/report-templates/create.hbs:5 routes/report-templates.js:231 +msgid "Create Report Template" +msgstr "Crea Template di report" + +#: views/report-templates/edit.hbs:4 views/templates/edit.hbs:3 +#: views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "Modifica Template" + +#: views/report-templates/edit.hbs:5 routes/report-templates.js:262 +msgid "Edit Report Template" +msgstr "Modifica template report" + +#: views/report-templates/edit.hbs:6 views/templates/edit.hbs:12 +msgid "Delete Template" +msgstr "Elimina template" + +#: views/report-templates/edit.hbs:7 +msgid "Update and Stay" +msgstr "Salva e continua" + +#: views/report-templates/edit.hbs:8 +msgid "Update and Leave" +msgstr "Salva ed esci" + +#: views/report-templates/partials/report-template-fields.hbs:2 +msgid "Template Name" +msgstr "Nome template" + +#: views/report-templates/partials/report-template-fields.hbs:6 +msgid "User selectable fields" +msgstr "Campi selezionabili dagli utenti" + +#: views/report-templates/partials/report-template-fields.hbs:7 +msgid "Data processing code" +msgstr "Codice che processa data" + +#: views/report-templates/partials/report-template-fields.hbs:8 +msgid "Rendering template" +msgstr "Template di rendering" + +#: views/report-templates/report-templates.hbs:5 +msgid "Blank" +msgstr "Vuoto" + +#: views/report-templates/report-templates.hbs:6 +msgid "All Subscribers" +msgstr "Tutti gli iscritti" + +#: views/report-templates/report-templates.hbs:7 +msgid "Grouped Subscribers" +msgstr "Iscritti raggruppati" + +#: views/report-templates/report-templates.hbs:8 +msgid "Export List as CSV" +msgstr "Esporta lista come CSV" + +#: views/report-templates/report-templates.hbs:9 views/reports/reports.hbs:4 +#: routes/report-templates.js:29 +msgid "Report Templates" +msgstr "Templates di report" + +#: 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 "Crea report" + +#: views/reports/edit.hbs:3 views/reports/edit.hbs:4 routes/reports.js:151 +msgid "Edit Report" +msgstr "Modifica Report" + +#: views/reports/edit.hbs:5 +msgid "Delete Report" +msgstr "Elimina report" + +#: views/reports/partials/report-fields.hbs:2 +msgid "Report Name" +msgstr "Nome report" + +#: 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 "" +"Seleziona una campagna nella tabella qui sopra cliccando sul numero della " +"riga." + +#: views/reports/partials/report-select-template.hbs:1 +msgid "Report Template" +msgstr "Template del report" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "Indirizzo del servizio (URL)" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "Inserisci l'URL a cui puoi accedere a questo servizio" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "Email amministratore" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "" +"Inserisci l'indirizzo mail che verra' utilizzato nel campo \"Da\" per i " +"messaggi di sistemi" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "Disabilita l'editor avanzato WYSIWYG" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "" +"Se selezionato l'editor del messaggio visualizza il codice HTML senza " +"anteprima" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "Disabilita i messaggi di conferma iscrizione" + +#: 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 "" +"Se selezionato il software non invia messaggi di avvenuta iscrizione e " +"cancellazione. Questo non disabilita l'invio di messaggi per i link di " +"conferma delle azioni." + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "ID Tracciamento" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "Inserisci il codice di tracciamento di Google Analytics" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "Annuncio pagina iniziale" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "Codice HTML visualizzato nell'header dell'home page" + +#: views/settings.hbs:17 +msgid "Campaign defaults" +msgstr "Valori predefiniti campagna" + +#: views/settings.hbs:18 +msgid "Sender name" +msgstr "Nome mittente" + +#: views/settings.hbs:19 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "Nome mittente, per esempio : Super Compagnia S.r.l." + +#: views/settings.hbs:20 +msgid "Default address" +msgstr "Indirizzo predefinito" + +#: views/settings.hbs:21 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "" +"Indirizzo fisico di contatto, per esempio: via Duomo 71, Milano, 20100 (MI)" + +#: views/settings.hbs:22 +msgid "Default \"from name\"" +msgstr "Valore predefinito nome mittente" + +#: views/settings.hbs:24 +msgid "Default \"from\" email" +msgstr "Valore predefinito email mittente" + +#: views/settings.hbs:26 +msgid "Default \"subject line\"" +msgstr "Valore predefinito oggetto" + +#: views/settings.hbs:28 +msgid "Default homepage (URL)" +msgstr "Valore predefinito sito (URL)" + +#: views/settings.hbs:29 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "" +"URL a cui reindirizzare gli utenti iscritti alla newsletter, per esempio " +"http://example.com" + +#: views/settings.hbs:30 +msgid "Mailer Settings" +msgstr "Impostazioni mailer" + +#: views/settings.hbs:31 +msgid "These settings are required to send out e-mail messages" +msgstr "Queste impostazioni sono richieste per inviare messaggi email" + +#: views/settings.hbs:32 +msgid "SMTP" +msgstr "" + +#: views/settings.hbs:33 +msgid "AWS SES" +msgstr "" + +#: views/settings.hbs:34 +msgid "Use SMTP for sending mail" +msgstr "Usa SMTP per spedire le email" + +#: views/settings.hbs:35 +msgid "Hostname" +msgstr "" + +#: views/settings.hbs:36 +msgid "Port" +msgstr "" + +#: views/settings.hbs:37 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "" + +#: views/settings.hbs:38 +msgid "Encryption" +msgstr "Crittografia" + +#: views/settings.hbs:39 +msgid "Disable SMTP authentication" +msgstr "Disabilita autenticazione SMTP" + +#: views/settings.hbs:40 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "Nome utente" + +#: views/settings.hbs:41 +msgid "Username, eg. myaccount@example.com" +msgstr "Nome utente (per esempio account@example.com)" + +#: views/settings.hbs:42 views/settings.hbs:43 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "" + +#: views/settings.hbs:44 +msgid "Use SES API for sending mail" +msgstr "" + +#: views/settings.hbs:45 +msgid "Access Key" +msgstr "" + +#: views/settings.hbs:46 +msgid "AWS Access Key Id" +msgstr "" + +#: views/settings.hbs:47 +msgid "Secret Key" +msgstr "" + +#: views/settings.hbs:48 +msgid "AWS Secret Access Key" +msgstr "" + +#: views/settings.hbs:49 +msgid "Region" +msgstr "Regione" + +#: views/settings.hbs:50 +msgid "Checking" +msgstr "Controllo" + +#: views/settings.hbs:51 +msgid "Check Mailer config" +msgstr "Controlla la configurazione del mailer" + +#: views/settings.hbs:52 +msgid "Advanced Mailer settings" +msgstr "Impostazioni Mailer avanzate" + +#: views/settings.hbs:53 +msgid "Log SMTP transactions" +msgstr "" + +#: views/settings.hbs:54 +msgid "Allow self-signed certificates" +msgstr "" + +#: views/settings.hbs:55 +msgid "Max connections" +msgstr "Massime connessioni" + +#: views/settings.hbs:56 +msgid "The count of max connections, eg. 10" +msgstr "Numero massimo di connessioni (per esempio 10)" + +#: views/settings.hbs:57 +msgid "" +"The count of maximum simultaneous connections to make against the SMTP " +"server (defaults to 5). This limit is per sending process." +msgstr "" + +#: views/settings.hbs:58 +msgid "Max messages" +msgstr "Massimo messaggi" + +#: views/settings.hbs:59 +msgid "The count of max messages, eg. 100" +msgstr "Il numero dei messaggi massimi, per esempio 1000" + +#: views/settings.hbs:60 +msgid "" +"The number of messages to send through a single connection before the " +"connection is closed and reopened (defaults to 100)" +msgstr "" + +#: views/settings.hbs:61 +msgid "Throttling" +msgstr "" + +#: views/settings.hbs:62 +msgid "Messages per hour eg. 1000" +msgstr "Messaggi all'ora" + +#: views/settings.hbs:63 +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 "" +"Numero di messaggi massimo da inviare all'ora. Lascia il campo vuoto o con 0 " +"per non rallentare. Se il tuo provider usa diversi limiti di velocità di " +"invio (messaggi al minuto o al secondo), converti il valore per adattarlo (1 " +"mail / secondo => 3600 mail / ora ). Questo valore è per ogni singolo " +"processo di invio." + +#: views/settings.hbs:64 +msgid "VERP bounce handling" +msgstr "" + +#: views/settings.hbs:65 +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 "" + +#: views/settings.hbs:66 +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 "" + +#: views/settings.hbs:67 +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 "" + +#: views/settings.hbs:68 +msgid "Use VERP to catch bounces" +msgstr "" + +#: views/settings.hbs:69 +msgid "Server hostname" +msgstr "" + +#: views/settings.hbs:70 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "" + +#: views/settings.hbs:71 +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 "" + +#: views/settings.hbs:72 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" + +#: views/settings.hbs:73 +msgid "GPG Signing" +msgstr "" + +#: views/settings.hbs:74 +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 "" + +#: views/settings.hbs:75 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" + +#: views/settings.hbs:76 +msgid "Private Key Passphrase" +msgstr "" + +#: views/settings.hbs:77 +msgid "Passphrase for the key if set" +msgstr "" + +#: views/settings.hbs:78 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "" + +#: views/settings.hbs:79 +msgid "GPG Private Key" +msgstr "" + +#: views/settings.hbs:81 +msgid "" +"This value is optional. If you do not provide a private key GPG encrypted " +"messages are sent without signing." +msgstr "" + +#: views/settings.hbs:82 +msgid "DKIM Signing by ZoneMTA" +msgstr "" + +#: views/settings.hbs:83 +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 "" + +#: views/settings.hbs:84 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" + +#: views/settings.hbs:85 +msgid "ZoneMTA DKIM API Key" +msgstr "" + +#: views/settings.hbs:86 +msgid "Some secret value" +msgstr "" + +#: views/settings.hbs:87 +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 "" + +#: views/settings.hbs:88 +msgid "DKIM domain" +msgstr "" + +#: views/settings.hbs:89 +msgid "Domain name for the DKIM key" +msgstr "" + +#: views/settings.hbs:90 +msgid "Leave blank to use the sender email address domain" +msgstr "" + +#: views/settings.hbs:91 views/settings.hbs:92 +msgid "DKIM key selector" +msgstr "" + +#: views/settings.hbs:93 +msgid "Signing is disabled without a valid selector value" +msgstr "" + +#: views/settings.hbs:94 +msgid "DKIM Private Key" +msgstr "" + +#: views/settings.hbs:96 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" + +#: views/subscription/mail-already-subscribed-html.mjml.hbs:1 +#: views/subscription/mail-already-subscribed-text.hbs:1 +#: lib/models/subscriptions.js:174 lib/models/subscriptions.js:895 +msgid "Email address already registered" +msgstr "Indirizzo email già registrato" + +#: 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 "" +"Abbiamo ricevuto una richiesta di iscrizione alla nostra newsletter. La tua " +"email è già registrata." + +#: 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 "" +"Se tu ricevi questa email per errore, cancellala semplicemente. La tua " +"registrazione non subirà alcuna modifica." + +#: 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 "Se vuoi modificare la tua iscrizione allora puoi" + +#: 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 "gestire le tue preferenze" + +#: 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 "oppure" + +#: 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 "cancellare l'iscrizione qui" + +#: 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 "Torna sul nostro sito" + +#: 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 "Per domande riguardanti questa newsletter, contatta:" + +#: 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 "Se vuoi modificare la tua iscrizione allora puoi:" + +#: 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 "Per favore conferma il cambio dell'indirizzo dell'iscrizione" + +#: 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ì, iscrivimi alla 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 "" +"Se ricevi questa email per errore, semplicemente cancellala. Non verrai " +"registrato se non clicchi sul link di conferma qui sopra." + +#: 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 "" +"Se ricevi questa email per errore, semplicemente cancellala. Non verrai " +"registrato se non clicchi sul link di conferma qui sopra." + +#: 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:432 +msgid "Please Confirm Subscription" +msgstr "Per favore conferma la tua iscrizione" + +#: 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ì, iscrivimi a questa lista" + +#: views/subscription/mail-confirm-unsubscription-html.mjml.hbs:1 +msgid "Please Confirm Unsubscription" +msgstr "Per favore conferma la cancellazione dell'iscrizione" + +#: 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ì, cancella la mia iscrizione alla 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 "" +"Se ricevi questo messaggio per errore, cancellalo pure. Non sarai cancellato " +"dalla tua iscrizione se non clicchi sul link di conferma." + +#: 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 "" +"Se ricevi questo messaggio per errore, cancellalo pure. Non sarai cancellato " +"dalla tua iscrizione se non clicchi sul link di conferma." + +#: 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 "Iscrizione confermata" + +#: views/subscription/mail-subscription-confirmed-html.mjml.hbs:2 +msgid "Your subscription to our list has been confirmed" +msgstr "La tua iscrizione alla nostra lista è stata confermata" + +#: 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 "La tua iscrizione alla nostra lista è stata confermata." + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:1 +#: views/subscription/mail-unsubscription-confirmed-text.hbs:1 +msgid "You Are Now Unsubscribed" +msgstr "La tua iscrizione è stata cancellata" + +#: views/subscription/mail-unsubscription-confirmed-html.mjml.hbs:2 +msgid "We have removed your email address from our list" +msgstr "Ti abbiamo rimosso dalla nostra 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 "Se ti sei cancellato per sbaglio, puoi iscriverti nuovamente qui:" + +#: views/subscription/mail-unsubscription-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "Abbiamo rimosso la tua email dalla nostra lista." + +#: views/subscription/partials/subscription-custom-fields.hbs:2 +msgid "want to change it?" +msgstr "vuoi cambiarlo?" + +#: views/subscription/partials/subscription-custom-fields.hbs:5 +msgid "Download signature verification key" +msgstr "Scarica la chiave di verifica" + +#: views/subscription/partials/subscription-custom-fields.hbs:7 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "" +"Inserisci la tua chiave pubblica GPG qui per cifrare i messaggi inviati al " +"tuo server" + +#: views/subscription/partials/subscription-custom-fields.hbs:8 +msgid "optional" +msgstr "opzionale" + +#: views/subscription/partials/subscription-flash-messages.hbs:1 +#: views/subscription/partials/subscription-flash-messages.hbs:3 +msgid "Warning!" +msgstr "Attenzione!" + +#: views/subscription/partials/subscription-flash-messages.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "" +"Se JavaScript non è abilitato, non verrà inviato alcun messaggio di conferma" + +#: views/subscription/partials/subscription-flash-messages.hbs:4 +msgid "JavaScript must be enabled in order for this form to work" +msgstr "" +"JavaScript deve essere abilitato per il funzionamento di questo modulo da " +"compilare" + +#: views/subscription/partials/subscription-manage-address-form.hbs:1 +msgid "Existing Email Address" +msgstr "Indirizzo email esistente" + +#: views/subscription/partials/subscription-manage-address-form.hbs:2 +msgid "New Email Address" +msgstr "Nuovo indirizzo email" + +#: views/subscription/partials/subscription-manage-address-form.hbs:3 +msgid "Your new email address" +msgstr "Il tuo nuovo indirizzo email" + +#: 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 "" +"Riceverai una richiesta di conferma al tuo nuovo indirizzo di posta e devi " +"confermarlo per far sì che la modifica abbia effetto" + +#: views/subscription/partials/subscription-manage-address-form.hbs:5 +#: views/subscription/web-manage-address.mjml.hbs:2 +msgid "Update Email Address" +msgstr "Aggiorna indirizzo email" + +#: views/subscription/partials/subscription-manage-form.hbs:1 +#: views/subscription/web-manage.mjml.hbs:2 +msgid "Update Profile" +msgstr "Aggiorna profilo" + +#: 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 "Iscrivita alla 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 "Quasi finito" + +#: 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 "" +"Dobbiamo confermare il tuo indirizzo email. Per completare l'iscrizione, " +"clicca sul link che ti abbiamo inviato al tuo indirizzo email." + +#: 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 "" +"Abbiamo bisogno che confermi il tuo indirizzo email. Per completare il " +"processo di rimossione, si prega di cliccare il link che ti abbiamo appena " +"spedito." + +#: views/subscription/web-manage-address.mjml.hbs:1 +msgid "Update Your Email Address" +msgstr "Aggiorna il tuo indirizzo email" + +#: views/subscription/web-manage.mjml.hbs:1 +msgid "Update Your Preferences" +msgstr "Aggiorna le tue preferenze" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:1 +msgid "Online Unsubscription Is Not Possible" +msgstr "La rimozione dell'iscrizione non è fattibile online" + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:2 +msgid "Please contact us at" +msgstr "Puoi contattarci all'indirizzo " + +#: views/subscription/web-manual-unsubscribe-notice.mjml.hbs:3 +msgid "to get removed from the list" +msgstr "per essere rimosso dalla lista" + +#: views/subscription/web-subscribe.mjml.hbs:1 +msgid "Subscribe to List" +msgstr "Iscriviti alla lista" + +#: views/subscription/web-subscribed-notice.mjml.hbs:3 +msgid "Thank you for subscribing!" +msgstr "Grazie per la tua iscrizione!" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:1 +msgid "Unsubscribe Successful" +msgstr "Rimozione eseguita con successo" + +#: views/subscription/web-unsubscribed-notice.mjml.hbs:2 +msgid "You have been removed from:" +msgstr "Se stato rimosso da:" + +#: views/subscription/web-updated-notice.mjml.hbs:1 +msgid "Profile Updated" +msgstr "Profilo Aggiornato" + +#: views/subscription/web-updated-notice.mjml.hbs:2 +msgid "Your profile information has been updated." +msgstr "Le informazioni del tuo profilo sono stato aggiornate." + +#: views/subscription/widget-subscribe.hbs:2 +msgid "Sending ..." +msgstr "Invio ..." + +#: views/subscription/widget-subscribe.hbs:3 +msgid "It looks like you are already subscribed to this list." +msgstr "Sembra che tu ti sia già iscritto a questa lista." + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "Nome Template" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "Nome di questo template eg. Newsletter promozioni" + +#: 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 "Commenti opzionali per questo template" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "Indietro ai templates" + +#: views/templates/edit.hbs:11 +msgid "Duplicate" +msgstr "Duplica" + +#: 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 "Trigger di automazione" + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "Seleziona una lista per questo trigger" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "Nome trigger" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "Nome per questo trigger, per esempio: iscritti inattivi" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "Commenti opzionali per questo trigger" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "Regola trigger" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "Attivazione trigger" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "dopo giorni:" + +#: 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 "Campagna" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "Azione trigger" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "Invia campagna" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "Modifica trigger" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "Indietro ai triggers" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "Il trigger è abilitato" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "Elimina trigger" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "Trigger azionato" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "Iscritti con trigger azionati" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "Iscritti che hanno azionato questo trigger" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "Ora azionamento Trigger" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "Campagna targetizzata" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "Conteggio trigger azionati" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "Questo account è controllato tramite LDAP." + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "Indirizzo email associato" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "Il tuo indirizzo email" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you lose your password" +msgstr "Questo indirizzo è utilizzato per il recupero della password" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "Cambia password" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "" +"Hai bisogno di completare questo form solo se vuoi cambiare la tua password " +"attuale" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "Password attuale" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "Nuova password" + +#: views/users/account.hbs:16 views/users/reset.hbs:8 +msgid "Confirm Password" +msgstr "Conferma password" + +#: views/users/account.hbs:17 views/users/reset.hbs:9 +msgid "Confirm New Password" +msgstr "Conferma nuova password" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "Sei sicuro? Il reset invaliderà i tokens che esistono al momento" + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "Sei sicuro?" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "Resetta token di accesso" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "Genera token di accesso" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "Token di accesso personale:" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "Token di accesso non generato" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "Note sull'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 "" +"La risposta API è una struttura JSON con proprietà error e " +"data. Se la risposta ha il valore error impostato, " +"la richiesta è fallita." + +#: 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 "" +"Devi definire il giusto Content-Type quando esegui una " +"richiesta. Puoi usare application/x-www-form-urlencoded per i " +"dati dei form normali, oppure application/json per JSON " +"payload. Non è supportato multipart/form-data." + +#: views/users/api.hbs:13 +msgid "Get list of subscriptions" +msgstr "Ottieni lista delle iscrizioni" + +#: views/users/api.hbs:14 +msgid "" +"Retrieve a list of subscriptions to the list referenced by :listId. All " +"fields of the subscribers will be returned. Note that custom fields will " +"have generated names." +msgstr "" +"Ottieni una lista delle iscrizioni alla lista :listId. Tutti i campi degli " +"iscritti saranno restituiti. Nota che i campi personalizzati avranno dei " +"nomi automaticamente generati." + +#: views/users/api.hbs:15 views/users/api.hbs:24 views/users/api.hbs:26 +#: views/users/api.hbs:39 views/users/api.hbs:41 views/users/api.hbs:47 +#: views/users/api.hbs:49 views/users/api.hbs:55 views/users/api.hbs:57 +#: views/users/api.hbs:67 views/users/api.hbs:78 views/users/api.hbs:80 +#: views/users/api.hbs:86 views/users/api.hbs:88 views/users/api.hbs:94 +msgid "arguments" +msgstr "argomenti" + +#: views/users/api.hbs:16 views/users/api.hbs:25 views/users/api.hbs:40 +#: views/users/api.hbs:48 views/users/api.hbs:56 views/users/api.hbs:68 +#: views/users/api.hbs:79 views/users/api.hbs:87 views/users/api.hbs:95 +msgid "your personal access token" +msgstr "il tuo token di accesso personale" + +#: views/users/api.hbs:17 views/users/api.hbs:69 +msgid "Start position" +msgstr "Posizionale di inizio" + +#: views/users/api.hbs:18 views/users/api.hbs:70 +msgid "optional, default 0" +msgstr "opzionale, 0 di default" + +#: views/users/api.hbs:19 +msgid "limit subscription count in response" +msgstr "limita il numero di iscrizioni in risposta" + +#: views/users/api.hbs:20 views/users/api.hbs:72 +msgid "optional, default 10000" +msgstr "opzionale, default 10000" + +#: views/users/api.hbs:22 +msgid "Add subscription" +msgstr "Aggiungi iscrizione" + +#: views/users/api.hbs:23 +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 "" +"Questa chiamata API aggiunge o aggiorna una iscrizione. I campi non inclusi " +"saranno lasciati come erano, quindi se si aggiorna LAST_NAME, FIRST_NAME " +"rimarrà invariato per quella iscrizione." + +#: views/users/api.hbs:27 views/users/api.hbs:42 views/users/api.hbs:50 +msgid "subscriber's email address" +msgstr "indirizzo email dell'iscritto" + +#: views/users/api.hbs:28 views/users/api.hbs:43 views/users/api.hbs:51 +#: views/users/api.hbs:59 views/users/api.hbs:82 views/users/api.hbs:90 +msgid "required" +msgstr "richiesto" + +#: views/users/api.hbs:29 +msgid "subscriber's first name" +msgstr "nome dell'iscritto" + +#: views/users/api.hbs:30 +msgid "subscriber's last name" +msgstr "cognome dell'iscritto" + +#: views/users/api.hbs:31 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" +"timezone dell'iscritto (esempio: \"Europe/Tallinn\", \"PST\" or \"UTC\"). Se " +"non impostato il valore di default è \"UTC\"" + +#: views/users/api.hbs:32 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" +"valore dei campi personalizzati. Usa si/no per i valori dei gruppi di " +"opzioni (checkboxes, radios, drop downs) " + +#: views/users/api.hbs:33 +msgid "Additional POST arguments" +msgstr "" + +#: views/users/api.hbs:34 +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 "" +"selezionare \"sì\" se vuoi essere sicuro che la mail venga salvata come " +"iscritta anche se precedentemente cancellata. Se la mail si era già " +"cancellata o bloccata, lo stato di iscrizione non cambierà." + +#: views/users/api.hbs:35 +msgid "" +"set to \"yes\" if you want to send confirmation email to the subscriber " +"before actually marking as subscribed" +msgstr "" +"selezionare \"sì\" se vuoi inviare la mail di conferma all'email " +"delliscrizione prima di salvarlo come iscritto" + +#: views/users/api.hbs:37 +msgid "Remove subscription" +msgstr "Rimuovi iscrizione" + +#: views/users/api.hbs:38 +msgid "This API call marks a subscription as unsubscribed" +msgstr "Questa chiamata API segna una iscrizione come rimossa." + +#: views/users/api.hbs:45 +msgid "Delete subscription" +msgstr "Cancella iscrizione" + +#: views/users/api.hbs:46 +msgid "This API call deletes a subscription" +msgstr "Questa API cancella una iscrizione" + +#: views/users/api.hbs:53 +msgid "Add new custom field" +msgstr "Aggiungi un campo personalizzato" + +#: views/users/api.hbs:54 +msgid "This API call creates a new custom field for a list." +msgstr "Questa chiamata API crea un nuovo campo personalizzato per una lista." + +#: views/users/api.hbs:58 +msgid "field name" +msgstr "nome campo" + +#: views/users/api.hbs:60 +msgid "one of the following types:" +msgstr "uno dei seguenti tipi:" + +#: views/users/api.hbs:61 +msgid "" +"If the type is 'option' then you also need to specify the parent element ID" +msgstr "" +"Se il tipo è 'option' devi anche specificare l'ID dell'elemento parente" + +#: views/users/api.hbs:62 +msgid "" +"Template for the group element. If not set, then values of the elements are " +"joined with commas" +msgstr "" +"Template per il gruppo di elementi. Se non impostato, allora i valori " +"verranno separati da una virgola" + +#: views/users/api.hbs:63 +msgid "" +"if not visible then the subscriber can not view or modify this value at the " +"profile page" +msgstr "" +"se non visibile l'iscritto non potrà modificare questo valore nella pagina " +"del profilo" + +#: views/users/api.hbs:65 +msgid "Get list of blacklisted emails" +msgstr "Ottieni emails in lista nera" + +#: views/users/api.hbs:66 +msgid "This API call get list of blacklisted emails." +msgstr "Questa chiamata API ottiene una lista delle email in lista nera." + +#: views/users/api.hbs:71 +msgid "limit emails count in response" +msgstr "limita il numero di emails nella risposta" + +#: views/users/api.hbs:73 +msgid "filter by part of email" +msgstr "filtra per parte della email" + +#: views/users/api.hbs:74 +msgid "optional, default ''" +msgstr "opzionale, default ''" + +#: views/users/api.hbs:77 +msgid "This API call either add emails to blacklist" +msgstr "Questa chiamata API aggiunge emails alla lista nera" + +#: views/users/api.hbs:81 views/users/api.hbs:89 +msgid "email address" +msgstr "indirizzo email" + +#: views/users/api.hbs:84 +msgid "Delete email from blacklist" +msgstr "Cancella email dalla lista nera." + +#: views/users/api.hbs:85 +msgid "This API call either delete emails from blacklist" +msgstr "Questa chiamata API cancella emails dalla lista nera" + +#: views/users/api.hbs:92 +msgid "Get the lists a user has subscribed to" +msgstr "Ottini le listi a cui un utente si è iscritto" + +#: views/users/api.hbs:93 +msgid "Retrieve the lists that the user with :email has subscribed to. " +msgstr "Ottieni le liste a cui l'utente con :email si è iscritto" + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "Resetta password" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "Resettare la password?" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "Gli accounts sono gestiti tramite LDAP." + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "Resetta password" + +#: 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 "" +"Si prega di inserire lo username o la email inserita al momento della " +"registrazione." + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "Ti invieremo una email che ti permetterà di cambiare password." + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "Username o email" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "Invia email di verifica" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "Ricordati di me" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "Password dimenticata?" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "Scegli la tua nuova password" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "Inserire una nuova password." + +#: lib/editor-helpers.js:17 routes/templates.js:95 +msgid "Could not find template with specified ID" +msgstr "Template con l'ID specificato non trovato" + +#: 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 "Campagna con l'ID specificato non trovata" + +#: lib/editor-helpers.js:47 routes/editorapi.js:320 +msgid "Invalid resource type" +msgstr "Tipo di risorsa invalida" + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "Codice %s di stato sbagliato " + +#: lib/helpers.js:33 +msgid "URL that points to the unsubscribe page" +msgstr "URL per la pagina di rimozione dall'iscrizione" + +#: lib/helpers.js:36 +msgid "URL that points to the preferences page of the subscriber" +msgstr "URL per la pagina delle preferenze dell'iscritto" + +#: lib/helpers.js:39 +msgid "URL to preview the message in a browser" +msgstr "URL per l'anteprima del messaggio nel browser" + +#: lib/helpers.js:45 lib/models/segments.js:31 +msgid "First name" +msgstr "Nome" + +#: lib/helpers.js:48 lib/models/segments.js:35 +msgid "Last name" +msgstr "Cognome" + +#: lib/helpers.js:51 +msgid "Full name (first and last name combined)" +msgstr "Nome e Cognome" + +#: lib/helpers.js:54 +msgid "Unique ID that identifies the recipient" +msgstr "ID unico che identifica il destinatario" + +#: lib/helpers.js:57 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "ID unico che identifica la lista usata per questa campagna" + +#: lib/helpers.js:60 +msgid "Unique ID that identifies current campaign" +msgstr "ID unico che identifica la campagna corrente" + +#: lib/helpers.js:68 lib/helpers.js:80 +msgid "content from an RSS entry" +msgstr "contenuto dal messaggio RSS" + +#: lib/helpers.js:71 +msgid "RSS entry title" +msgstr "Titolo RSS entry" + +#: lib/helpers.js:74 +msgid "RSS entry date" +msgstr "" + +#: lib/helpers.js:77 +msgid "RSS entry link" +msgstr "" + +#: lib/helpers.js:83 +msgid "RSS entry summary" +msgstr "" + +#: lib/helpers.js:86 +msgid "RSS entry image URL" +msgstr "" + +#: lib/mailer.js:245 +msgid "Invalid mail transport" +msgstr "Trasporto mail invalido" + +#: 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 "ID campagna mancante" + +#: lib/models/campaigns.js:241 +msgid "Emtpy or too large attahcment" +msgstr "Allegato troppo grande o mancante" + +#: lib/models/campaigns.js:408 lib/models/campaigns.js:600 +msgid "Campaign Name must be set" +msgstr "Il nome della campagna è obbligatorio" + +#: lib/models/campaigns.js:412 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "" + +#: lib/models/campaigns.js:568 +msgid "Selected template not found" +msgstr "Template selezionato non trovato" + +#: lib/models/campaigns.js:924 +msgid "Invalid or missing message ID" +msgstr "ID messaggio mancante o invalido" + +#: lib/models/campaigns.js:1065 +msgid "Unrecognized message status" +msgstr "Stato del messaggio non riconosciuto" + +#: lib/models/confirmations.js:27 +msgid "Could not store confirmation data" +msgstr "Impossibile salvare i dati di conferma" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "Drop Down" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "Data (MM/GG/AAAA)" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "Valore JSON per visualizzazione personalizzata" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "Opzione" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/forms.js:46 lib/models/lists.js:115 lib/models/lists.js:150 +#: lib/models/lists.js:264 lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:79 lib/models/subscriptions.js:390 +#: lib/models/subscriptions.js:566 lib/models/subscriptions.js:657 +#: lib/models/subscriptions.js:710 lib/models/subscriptions.js:773 +#: lib/models/subscriptions.js:816 +msgid "Missing List ID" +msgstr "ID lista mancante" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "Il campo Option richiede la selezione di un gruppo" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "ID campo manvante" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "Il nome del campo deve essere impostato" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "Campo personalizzato non trovato" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "Tipo di colonna %s non riconosciuto" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "nome colonna mancante" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "ID lista mancante" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "ID lista non trovato" + +#: 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 "ID Form mancante" + +#: lib/models/forms.js:121 lib/models/forms.js:205 +msgid "Form Name must be set" +msgstr "Il nome del form è obbligatorio" + +#: lib/models/forms.js:307 +msgid "Custom form not found" +msgstr "Form personalizzato non trovato" + +#: 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 "Campagna non trovata" + +#: lib/models/links.js:345 routes/lists.js:181 services/sender.js:312 +msgid "List not found" +msgstr "Lista non trovat" + +#: lib/models/links.js:353 +msgid "Subscription not found" +msgstr "Iscrizione non trovata" + +#: lib/models/lists.js:166 lib/models/lists.js:210 +msgid "List Name must be set" +msgstr "Il nome della lista è obbligatorio" + +#: lib/models/lists.js:293 +msgid "Missing List CID" +msgstr "CID lista mancante" + +#: lib/models/report-templates.js:26 lib/models/report-templates.js:70 +#: lib/models/report-templates.js:142 +msgid "Missing report template ID" +msgstr "ID template di reportistica mancante" + +#: lib/models/report-templates.js:77 +msgid "Report template name must be set" +msgstr "Il nome del template di report è obbligatorio" + +#: lib/models/reports.js:40 lib/models/reports.js:110 lib/models/reports.js:188 +msgid "Missing report ID" +msgstr "ID report mancante" + +#: lib/models/reports.js:116 +msgid "Report name must be set" +msgstr "Il nome del report è obblicatorio" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "Nazione di registrazione" + +#: lib/models/segments.js:19 lib/models/triggers.js:12 +msgid "Sign up date" +msgstr "Data registrazione" + +#: lib/models/segments.js:23 lib/models/triggers.js:16 +msgid "Latest open" +msgstr "Ultima apertura" + +#: lib/models/segments.js:27 lib/models/triggers.js:20 +msgid "Latest click" +msgstr "Ultimo click" + +#: 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 "ID segmento mancante" + +#: lib/models/segments.js:85 lib/models/segments.js:549 +#: lib/models/segments.js:658 +msgid "Segment not found" +msgstr "Segmento non trovato" + +#: 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 giorni a partire da oggi" + +#: 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 giorni fa" + +#: lib/models/segments.js:148 lib/models/segments.js:410 +msgid "today" +msgstr "oggi" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "Tipo di regola del segmento non valido" + +#: 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 "Segmento selezionato non trovato" + +#: 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 di regola non valida" + +#: lib/models/segments.js:358 lib/models/segments.js:434 +#: lib/models/segments.js:524 +msgid "Missing Rule ID" +msgstr "ID regola mancante" + +#: lib/models/segments.js:374 +msgid "Specified rule not found" +msgstr "Regola specificata non trovata" + +#: lib/models/segments.js:385 +msgid "Specified segment not found" +msgstr "Segmento selezionato non trovato" + +#: lib/models/segments.js:445 +msgid "Selected rule not found" +msgstr "Regola selezionata non trovata" + +#: lib/models/subscriptions.js:257 lib/models/subscriptions.js:287 +#: lib/models/subscriptions.js:394 +msgid "Missing Subscription ID" +msgstr "ID iscrizione mancante" + +#: lib/models/subscriptions.js:315 +msgid "Missing Subscription email address" +msgstr "Indirizzo email di iscrizione mancante" + +#: lib/models/subscriptions.js:570 lib/models/subscriptions.js:820 +msgid "Missing subscription ID" +msgstr "ID di iscrizione mancante" + +#: lib/models/subscriptions.js:661 lib/models/subscriptions.js:714 +#: lib/models/subscriptions.js:750 +msgid "Missing Import ID" +msgstr "ID di import mancante" + +#: lib/models/subscriptions.js:842 +msgid "Unknown subscription ID" +msgstr "ID iscrizione sconosciuto" + +#: lib/models/subscriptions.js:847 routes/subscription.js:639 +msgid "Nothing seems to be changed" +msgstr "Nulla sembra essere cambiato" + +#: lib/models/subscriptions.js:913 routes/subscription.js:473 +#: routes/subscription.js:545 routes/subscription.js:581 +#: routes/subscription.js:697 routes/subscription.js:772 +msgid "Subscription not found in this list" +msgstr "Iscrizione non trovata nella lista" + +#: lib/models/templates.js:26 lib/models/templates.js:102 +#: lib/models/templates.js:157 +msgid "Missing Template ID" +msgstr "ID template mancante" + +#: lib/models/templates.js:55 lib/models/templates.js:106 +msgid "Template Name must be set" +msgstr "Nome template obbligatorio" + +#: lib/models/templates.js:147 +msgid "Template does not exist" +msgstr "Il template non esiste" + +#: lib/models/triggers.js:29 +msgid "Has Opened" +msgstr "Ha aperto" + +#: lib/models/triggers.js:32 +msgid "Has Clicked" +msgstr "Ha cliccato" + +#: lib/models/triggers.js:35 +msgid "Not Opened" +msgstr "Non aperto" + +#: lib/models/triggers.js:38 +msgid "Not Clicked" +msgstr "Non cliccato" + +#: lib/models/triggers.js:178 lib/models/triggers.js:215 +msgid "Missing or invalid list ID" +msgstr "ID Lista mancante on non valido" + +#: lib/models/triggers.js:182 lib/models/triggers.js:267 +msgid "Days in the past are not allowed" +msgstr "Giorni nel passato non sono permessi" + +#: lib/models/triggers.js:186 lib/models/triggers.js:207 +#: lib/models/triggers.js:271 lib/models/triggers.js:292 +msgid "Missing or invalid trigger rule" +msgstr "Regola di trigger mancante o non valido" + +#: lib/models/triggers.js:193 lib/models/triggers.js:278 +msgid "Invalid subscription configuration" +msgstr "Configurazione di iscrizione non valida" + +#: lib/models/triggers.js:200 lib/models/triggers.js:285 +msgid "Invalid campaign configuration" +msgstr "Configurazione della campagna non valid" + +#: lib/models/triggers.js:203 lib/models/triggers.js:288 +msgid "A campaing can not be a target for itself" +msgstr "Una campagna non può' essere il target di sè stessa" + +#: lib/models/triggers.js:236 +msgid "Could not store trigger row" +msgstr "Impossibile salvare la riga di trigger" + +#: lib/models/triggers.js:253 +msgid "Missing or invalid Trigger ID" +msgstr "ID trigger mancante o non valido" + +#: lib/models/triggers.js:320 +msgid "Missing Trigger ID" +msgstr "ID trigger mancante" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "Impossibile salvare riga utente" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "Indirizzo email obbligatorio" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "Controllo dati inseriti fallito" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" +"Impossibile cambiare indirizzo email poiché esiste un account già associato " +"all'indirizzo" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "Password corrente errata" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "Nuova password non impostata" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "Le password non corrispondono" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "ID utente non impostato" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "Il nome utente è obbligatorio" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "Richiesta cambio password del Mailer" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "nome utente o tocken di accesso invalido" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "Nuova password non valida" + +#: lib/passport.js:49 +msgid "%s logged out" +msgstr "%s è uscito" + +#: lib/passport.js:63 +msgid "Failed to authenticate user" +msgstr "Impossibile verificare utente" + +#: lib/passport.js:79 +msgid "Logged in as %s" +msgstr "Connesso come %s" + +#: lib/passport.js:179 +msgid "Incorrect username or password" +msgstr "Nome utente o password errata" + +#: lib/subscription-mail-helpers.js:28 +msgid "%s: Subscription Confirmed" +msgstr "%s: Iscrizione confermata" + +#: lib/subscription-mail-helpers.js:39 +msgid "%s: Email Address Already Registered" +msgstr "%s: Indirizzo email già iscritto" + +#: lib/subscription-mail-helpers.js:49 +msgid "%s: Please Confirm Email Change in Subscription" +msgstr "%s: Per favore conferma il cambio di indirizzo email nell'iscrizione" + +#: lib/subscription-mail-helpers.js:59 +msgid "%s: Please Confirm Subscription" +msgstr "%s: Per favore conferma la tua iscrizione" + +#: lib/subscription-mail-helpers.js:69 +msgid "%s: Please Confirm Unsubscription" +msgstr "%s: Per favore conferma l'intenzione di rimuovere l'iscrizione" + +#: lib/subscription-mail-helpers.js:76 +msgid "%s: Unsubscription Confirmed" +msgstr "%s: Rimozione dell'iscrizione confermata" + +#: lib/tools.js:154 +msgid "Blocked email address \"%s\"" +msgstr "Email \"%s\" bloccata" + +#: lib/tools.js:163 +msgid "Invalid email address \"%s\"." +msgstr "Email \"%s\" invalida." + +#: lib/tools.js:166 +msgid "MX record not found for domain" +msgstr "Non trovo gli MX records per il dominio" + +#: lib/tools.js:169 +msgid "Address domain not found" +msgstr "Dominio dell'indirizzo non trovato" + +#: lib/tools.js:172 +msgid "Address domain name is required" +msgstr "Il dominio dell'indirizzo è obbligatorio" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:230 +msgid "Not Found" +msgstr "Non trovato" + +#: routes/archive.js:121 services/sender.js:449 +msgid "Received status code %s from %s" +msgstr "Ricevuto codice di stato %s da %s" + +#: routes/archive.js:153 routes/campaigns.js:894 +msgid "Attachment not found" +msgstr "Allegato non trovato" + +#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:32 +#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:15 +#: 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 "Devi aver effettuato l'accesso per accedere ai contenuti riservati" + +#: routes/campaigns.js:117 +msgid "Could not create campaign" +msgstr "Impossibile creare la campagna" + +#: routes/campaigns.js:120 +msgid "Campaign “%s” created" +msgstr "Campagna \"%s\" creata" + +#: routes/campaigns.js:209 +msgid "Campaign settings updated" +msgstr "Impostazioni della campagna aggiornate" + +#: routes/campaigns.js:211 +msgid "Campaign settings not updated" +msgstr "Impostazioni della campagna non aggiornata" + +#: routes/campaigns.js:227 routes/campaigns.js:744 +msgid "Campaign deleted" +msgstr "Campagna eliminata" + +#: routes/campaigns.js:229 routes/campaigns.js:746 +msgid "Could not delete specified campaign" +msgstr "Impossibile cancellare la campagna selezionata" + +#: routes/campaigns.js:248 +msgid "Idling" +msgstr "Aspettando" + +#: routes/campaigns.js:251 +msgid "Scheduled" +msgstr "In programma" + +#: routes/campaigns.js:257 +msgid "Paused" +msgstr "In pausa" + +#: routes/campaigns.js:259 +msgid "Inactive" +msgstr "Inattiva" + +#: routes/campaigns.js:261 +msgid "Active" +msgstr "Attiva" + +#: routes/campaigns.js:263 +msgid "Other" +msgstr "Altro" + +#: routes/campaigns.js:421 +msgid "Unknown status selector" +msgstr "Selettore di stato non trovato" + +#: routes/campaigns.js:762 +msgid "Scheduled sending" +msgstr "Invio programmato" + +#: routes/campaigns.js:764 +msgid "Could not schedule sending" +msgstr "Impossibile programmare l'invio" + +#: routes/campaigns.js:776 +msgid "Sending resumed" +msgstr "Invio ripreso" + +#: routes/campaigns.js:778 +msgid "Could not resume sending" +msgstr "Impossibile riprendere invio" + +#: routes/campaigns.js:790 +msgid "Sending reset" +msgstr "Reset invio" + +#: routes/campaigns.js:792 +msgid "Could not reset sending" +msgstr "Impossibile resettare l'invio" + +#: routes/campaigns.js:804 routes/campaigns.js:832 +msgid "Sending paused" +msgstr "Invio in pausa" + +#: routes/campaigns.js:806 routes/campaigns.js:834 +msgid "Could not pause sending" +msgstr "Impossibile mettere in pausa l'invio" + +#: routes/campaigns.js:818 +msgid "Sending activated" +msgstr "Invio attivato" + +#: routes/campaigns.js:820 +msgid "Could not activate sending" +msgstr "Impossibile attivare l'invio" + +#: routes/campaigns.js:855 +msgid "Attachment uploaded" +msgstr "Allegato caricato" + +#: routes/campaigns.js:857 +msgid "Could not store attachment" +msgstr "Impossibile salvare allegato" + +#: routes/campaigns.js:874 +msgid "Attachment deleted" +msgstr "Allegato eliminato" + +#: routes/campaigns.js:876 +msgid "Could not delete attachment" +msgstr "Impossibile eliminare allegato" + +#: routes/editorapi.js:38 +msgid "Invalid editor name" +msgstr "Nome editor invalido" + +#: routes/editorapi.js:146 +msgid "Method not supported" +msgstr "Metodo non supportato" + +#: routes/editorapi.js:381 +msgid "Invalid resource type or ID" +msgstr "ID o tipo di risorsa invalido" + +#: 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 lista selezionata non trovata" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "Impossibile creare campo personalizzato" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "Campo selezionato non trovato" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "Impostazioni campo aggiornate" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "Impostazioni campo non aggiornate" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "Campo personalizzato eliminato" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "Impossibile eliminare campo specificato" + +#: routes/forms.js:78 +msgid "Could not create custom form" +msgstr "Impossibile creare campo personalizzato" + +#: routes/forms.js:105 +msgid "Selected form not found" +msgstr "Form selezionato non trovato" + +#: routes/forms.js:136 +msgid "The plaintext version for this email" +msgstr "La versione di solo testo della mail" + +#: routes/forms.js:137 +msgid "Custom forms use MJML for formatting" +msgstr "I form personalizzati usano MJML per la formattazione" + +#: routes/forms.js:138 +msgid "See the MJML documentation here" +msgstr "" +"Guarda la documentazione di MJML qui" + +#: routes/forms.js:146 +msgid "Layout" +msgstr "Layout" + +#: routes/forms.js:152 +msgid "Form Input Style" +msgstr "Stile input del form" + +#: routes/forms.js:154 +msgid "" +"This CSS stylesheet defines the appearance of form input elements and alerts" +msgstr "" +"Questo stylesheet CSS definisce lo stile degli elementi di input dei form e " +"degli alert" + +#: routes/forms.js:160 +msgid "Web - Subscribe" +msgstr "Web - Iscriviti" + +#: routes/forms.js:165 +msgid "Web - Confirm Subscription Notice" +msgstr "Web - Notifica di conferma dell'iscrizione" + +#: routes/forms.js:170 +msgid "Mail - Confirm Subscription (MJML)" +msgstr "Mail - Conferma di iscrizione (MJML)" + +#: routes/forms.js:175 +msgid "Mail - Confirm Subscription (Text)" +msgstr "Mail - Conferma di iscrizione (Testo)" + +#: routes/forms.js:180 +msgid "Mail - Already Subscribed (MJML)" +msgstr "Mail - Già iscritto (MJML)" + +#: routes/forms.js:185 +msgid "Mail - Already Subscribed (Text)" +msgstr "Mail - Già iscritto (Testo)" + +#: routes/forms.js:190 +msgid "Web - Subscribed Notice" +msgstr "Web - Notifica di iscrizione" + +#: routes/forms.js:195 +msgid "Mail - Subscription Confirmed (MJML)" +msgstr "Mail - Conferma di iscrizione (MJML)" + +#: routes/forms.js:200 +msgid "Mail - Subscription Confirmed (Text)" +msgstr "Mail - Conferma di iscrizione (Testo)" + +#: routes/forms.js:208 +msgid "Web - Manage Preferences" +msgstr "Web - Gestisci l preferenze" + +#: routes/forms.js:213 +msgid "Web - Manage Address" +msgstr "Web - Gestisci indirizzo" + +#: routes/forms.js:218 +msgid "Web - Updated Notice" +msgstr "Web - Notifica di aggiornamento" + +#: routes/forms.js:226 +msgid "Web - Unsubscribe" +msgstr "Web - Rimozione iscrizione" + +#: routes/forms.js:231 +msgid "Web - Confirm Unsubscription Notice" +msgstr "Web - Conferma la notifica di rimozione dell'iscrizione" + +#: routes/forms.js:236 +msgid "Mail - Confirm Unsubscription (MJML)" +msgstr "Mail - Conferma la rimozione dell'iscrizione (MJML)" + +#: routes/forms.js:241 +msgid "Mail - Confirm Unsubscription (Text)" +msgstr "Mail - Conferma la rimozione dell'iscrizione (Text)" + +#: routes/forms.js:246 +msgid "Mail - Confirm Address Change (MJML)" +msgstr "Mail - Conferma il cambio di indirizzo (MJML)" + +#: routes/forms.js:251 +msgid "Mail - Confirm Address Change (Text)" +msgstr "Mail - Conferma il cambio di indirizzo (Testo)" + +#: routes/forms.js:256 +msgid "Web - Unsubscribed Notice" +msgstr "Web - Notifica di rimozione" + +#: routes/forms.js:261 +msgid "Mail - Unsubscription Confirmed (MJML)" +msgstr "Mail - Rimozione confermata (MJML)" + +#: routes/forms.js:266 +msgid "Mail - Unsubscription Confirmed (Text)" +msgstr "Mail - Rimozione confermata (Text)" + +#: routes/forms.js:271 +msgid "Web - Manual Unsubscribe Notice" +msgstr "Web - Notifica di rimozione manuale" + +#: routes/forms.js:309 +msgid "Form settings updated" +msgstr "Impostazioni form aggiornate" + +#: routes/forms.js:311 +msgid "Form settings not updated" +msgstr "Impostazioni form non aggiornate" + +#: routes/forms.js:327 +msgid "Custom form deleted" +msgstr "Form personalizzato non cancellato" + +#: routes/forms.js:329 +msgid "Could not delete specified form" +msgstr "Impossibile eliminare il form specificato" + +#: routes/index.js:11 +msgid "Self Hosted Newsletter App" +msgstr "Applicazione di newsletter self-hosted" + +#: routes/links.js:39 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "Oops, non riusciamo a trovare un link per l'URL che hai cliccato" + +#: routes/lists.js:82 +msgid "Could not create list" +msgstr "Impossibile creare lista" + +#: routes/lists.js:85 +msgid "List created" +msgstr "Lista creata" + +#: 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:657 routes/lists.js:714 routes/lists.js:741 +msgid "Could not find list with specified ID" +msgstr "Impossibile trovare la lista dell'ID specificato" + +#: routes/lists.js:122 +msgid "List settings updated" +msgstr "Impostazioni lista aggiornate" + +#: routes/lists.js:124 +msgid "List settings not updated" +msgstr "Impostazioni lista non aggiornate" + +#: routes/lists.js:142 +msgid "List deleted" +msgstr "Lista cancellata" + +#: routes/lists.js:144 +msgid "Could not delete specified list" +msgstr "Impossibile cancellare la lista specificata" + +#: routes/lists.js:206 +msgid "Unknown" +msgstr "Sconosciuto" + +#: routes/lists.js:206 +msgid "Complained" +msgstr "Reclamato" + +#: routes/lists.js:237 +msgid "Invalid key" +msgstr "Chiave invalida" + +#: routes/lists.js:239 +msgid "Expired key" +msgstr "Chiave scaduta" + +#: routes/lists.js:241 +msgid "Revoked key" +msgstr "Chiave revocate" + +#: routes/lists.js:288 +msgid "Force Subscribe" +msgstr "Forza iscrizione" + +#: routes/lists.js:291 +msgid "Initializing" +msgstr "Inizializzando" + +#: routes/lists.js:294 +msgid "Initialized" +msgstr "Inizializzato" + +#: routes/lists.js:297 +msgid "Importing" +msgstr "Importando" + +#: routes/lists.js:303 +msgid "Errored" +msgstr "Errore" + +#: routes/lists.js:381 routes/lists.js:450 routes/lists.js:475 +msgid "Could not find subscriber with specified ID" +msgstr "Iscritto con ID specificato non trovato" + +#: routes/lists.js:427 +msgid "Could not add subscription" +msgstr "Impossibile aggiungere iscrizione" + +#: routes/lists.js:432 +msgid "%s was successfully added to your list" +msgstr "%s aggiunto alla lista con successo" + +#: routes/lists.js:434 +msgid "%s was not added to your list" +msgstr "%s non aggiunto alla lista" + +#: routes/lists.js:456 +msgid "Could not unsubscribe user" +msgstr "Impossibile rimuovere iscrizione utente" + +#: routes/lists.js:459 +msgid "%s was successfully unsubscribed from your list" +msgstr "Rimozione iscrizione % dalla lista avvenuta con successo" + +#: routes/lists.js:479 +msgid "%s was successfully removed from your list" +msgstr "Iscrizione %s cancellata con successo" + +#: routes/lists.js:491 +msgid "Another subscriber with email address %s already exists" +msgstr "Una iscrizione con l'email %s esiste già" + +#: routes/lists.js:498 +msgid "Subscription settings updated" +msgstr "Impostazioni di iscrizione aggiornate" + +#: routes/lists.js:500 +msgid "Subscription settings not updated" +msgstr "Impostazioni di iscrizione non aggiornate" + +#: routes/lists.js:542 routes/lists.js:663 routes/lists.js:699 +#: routes/lists.js:727 routes/lists.js:747 +msgid "Could not find import data with specified ID" +msgstr "Impossibile trovare dati di importo per l'ID specificato" + +#: routes/lists.js:573 +msgid "Could not process CSV" +msgstr "Impossibile elaborare CSV" + +#: routes/lists.js:589 +msgid "Could not create importer" +msgstr "Impossibile creare una importazione" + +#: routes/lists.js:643 +msgid "Empty file" +msgstr "File vuoto" + +#: routes/lists.js:646 +msgid "Too few rows" +msgstr "Troppe poche righe" + +#: routes/lists.js:703 +msgid "Import started" +msgstr "Importazione iniziato" + +#: routes/lists.js:731 +msgid "Import restarted" +msgstr "Importazione riniziato" + +#: routes/lists.js:797 +msgid "One-step (i.e. no email with confirmation link)" +msgstr "Uno step (no email con link di conferma)" + +#: routes/lists.js:803 +msgid "" +"One-step with unsubscription form (i.e. no email with confirmation link)" +msgstr "Uno step con form di cancellazione (no email con link di conferma)" + +#: routes/lists.js:809 +msgid "Two-step (i.e. an email with confirmation link will be sent)" +msgstr "Due step (una email con link di conferma viene inviata)" + +#: routes/lists.js:815 +msgid "" +"Two-step with unsubscription form (i.e. an email with confirmation link will " +"be sent)" +msgstr "" +"Form di cancellazione a due step (una mail con link di conferma viene " +"inviata)" + +#: routes/lists.js:821 +msgid "" +"Manual (i.e. unsubscription has to be performed by the list administrator)" +msgstr "" +"Manuale (p.es. la cancellazione deve essere eseguita dall'amministratore " +"della lista)" + +#: routes/report-templates.js:246 +msgid "Could not create report template" +msgstr "Impossibile creare il template di report" + +#: routes/report-templates.js:249 +msgid "Report template “%s” created" +msgstr "Template report \"%s\" creato" + +#: routes/report-templates.js:257 +msgid "Could not find report template with specified ID" +msgstr "Impossibile trovare il template del report specificato" + +#: routes/report-templates.js:280 +msgid "Report template updated" +msgstr "Template report aggiornato" + +#: routes/report-templates.js:282 +msgid "Report template not updated" +msgstr "Template report non aggiornato" + +#: routes/report-templates.js:298 +msgid "Report template deleted" +msgstr "Template report eliminato" + +#: routes/report-templates.js:300 +msgid "Could not delete specified report template" +msgstr "Impossibile eliminare il template del report" + +#: routes/reports.js:124 routes/reports.js:130 +msgid "Could not create report" +msgstr "Impossibile creare il report" + +#: routes/reports.js:135 +msgid "Report “%s” created" +msgstr "Creato report \"%s\"" + +#: 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 "Impossibile trovare il report con l'ID specificato" + +#: routes/reports.js:188 routes/reports.js:194 +msgid "Could not update report" +msgstr "Impossibile aggiornare il report" + +#: routes/reports.js:197 +msgid "Report updated" +msgstr "Report aggiornato" + +#: routes/reports.js:199 +msgid "Report not updated" +msgstr "Report non aggiornato" + +#: routes/reports.js:212 +msgid "Report deleted" +msgstr "Report eliminato" + +#: routes/reports.js:214 +msgid "Could not delete specified report" +msgstr "Impossibile eliminare il report specificato" + +#: routes/reports.js:230 +msgid "Could not find report template" +msgstr "Template di reportistica non trovato" + +#: routes/reports.js:260 +msgid "Unknown type of template" +msgstr "Tipo di template sconosciuto" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "Impossibile creare segmento" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "Segmento creato" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "Segmento selezionato non trovato" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "Impostazioni segmento aggiornate" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "Impostazioni segmento non aggiornate" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "Segmento cancellato" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "Impossibile eliminare il segmento specifcato" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "Impossibile creare la regola" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "Regola creata" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "Impostazioni regola aggiornate" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "Impostazioni regola non aggiornate" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "Regola cancellata" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "Impossibile eliminare la regola specificata" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "Usa TLS" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "usualmente selezionata per la porta 465" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "Usa STARTTLS" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "usualmente selezionate per le porte 587 e 25" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "Non usare crittografia" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "Impostazioni aggiornate" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "Tipo di trasporto mail invalido" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "Chiave di accesso invalida" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "Credenziali AWS invalide" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "Connessione rifiutata, controlla hostname e porta." + +#: 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 "" +"Non ho ricevuto il messaggio di saluto dal server. Questo può succedere " +"quando ci si connette ad una porta TLS senza utilizzare TLS." + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "Non ho ricevuto il messaggio di saluto dal server." + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" +"Tempo di connessione scaduto. Controlla il tuo firewall, le porte sono " +"probabilmente chiuse." + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "" +"Autenticazione non accettata, il server richiede l'utilizzo di STARTTLS." + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "Autenticazione fallita, controlla il nome utente e la password." + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "Verifica delle impostazioni di invio mail fallita." + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "Il Server ha risposto con: \"%s\"" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "" +"Impostazioni di invio mail verificate, il software è pronto per inviare " +"delle mail!" + +#: routes/subscription.js:33 +msgid "Not allowed by CORS" +msgstr "Non permesso dalle impostazioni CORS" + +#: routes/subscription.js:61 routes/subscription.js:176 +#: routes/subscription.js:286 routes/subscription.js:382 +#: routes/subscription.js:459 routes/subscription.js:535 +#: routes/subscription.js:566 routes/subscription.js:626 +#: routes/subscription.js:682 routes/subscription.js:760 +#: routes/subscription.js:897 +msgid "Selected list not found" +msgstr "Lista selezionata non trovata" + +#: routes/subscription.js:93 +msgid "Could not save subscription" +msgstr "Impossibile salvare l'iscrizione" + +#: routes/subscription.js:118 +msgid "Subscriber info corrupted or missing" +msgstr "Informazioni iscritto corrotte o mancanti" + +#: routes/subscription.js:136 +msgid "Email address changed" +msgstr "Indirizzo email aggiornato" + +#: routes/subscription.js:179 routes/subscription.js:385 +msgid "The list does not allow public subscriptions." +msgstr "L'iscrizione alla lista non è pubblica." + +#: routes/subscription.js:355 routes/subscription.js:357 +msgid "Email address not set" +msgstr "Indirizzo email non impostato" + +#: routes/subscription.js:653 +msgid "" +"An email with further instructions has been sent to the provided address" +msgstr "" +"Una email con ulteriori istruzioni è stata inviata all'indirizzo fornito" + +#: routes/subscription.js:861 routes/subscription.js:877 +msgid "Public key is not set" +msgstr "la chiave pubblica non è impostata" + +#: routes/templates.js:84 +msgid "Could not create template" +msgstr "Impossibile creare template" + +#: routes/templates.js:87 +msgid "Template created" +msgstr "Template creato" + +#: routes/templates.js:126 +msgid "Template settings updated" +msgstr "Impostazioni template aggiornate" + +#: routes/templates.js:128 +msgid "Template settings not updated" +msgstr "Impostazioni template non aggiornate" + +#: routes/templates.js:144 +msgid "Template duplicated" +msgstr "Template duplicato" + +#: routes/templates.js:146 +msgid "Could not duplicate specified template" +msgstr "Impossibile duplicare il template specificato" + +#: routes/templates.js:157 +msgid "Template deleted" +msgstr "Template cancellato" + +#: routes/templates.js:159 +msgid "Could not delete specified template" +msgstr "Impossibile eliminare il template selezionato" + +#: routes/triggers.js:62 routes/triggers.js:79 routes/triggers.js:154 +msgid "Could not find selected list" +msgstr "Lista selezionata non trovata" + +#: routes/triggers.js:131 +msgid "Could not create trigger" +msgstr "Impossibile creare il trigger" + +#: routes/triggers.js:138 +msgid "Trigger “%s” created" +msgstr "Trigger \"%s\" creato" + +#: routes/triggers.js:214 +msgid "Trigger settings updated" +msgstr "Impostazioni trigger aggiornate" + +#: routes/triggers.js:216 +msgid "Trigger settings not updated" +msgstr "Impostazioni trigger non aggiornate" + +#: routes/triggers.js:228 +msgid "Trigger deleted" +msgstr "Trigger cancellato" + +#: routes/triggers.js:230 +msgid "Could not delete specified trigger" +msgstr "Impossibile eliminare il trigger specificato" + +#: routes/triggers.js:242 +msgid "Could not find trigger with specified ID" +msgstr "Trigger non trovato con l'ID specificato" + +#: routes/triggers.js:255 +msgid "Trigger not found" +msgstr "Trigger non trovato" + +#: 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 "" +"Una mail con le istruzioni di ripristino password sono state inviate alla " +"tua mail, se esiste nel sistema." + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "Token sconosciuto o scaduto" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "La tua password è stata cambiata con successo" + +#: routes/users.js:87 +msgid "User data not found" +msgstr "Dati utente non trovati" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "Token di accesso aggiornati" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "Token di access non aggiornati" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "Informazioni account aggiornate" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "Informazioni account non aggiornate" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "Errore feed %s" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "Trovato un nuovo messaggio %s per la campagna dal feed" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "Nulla di nuovo dal feed" + +#: services/feedcheck.js:146 +msgid "RSS entry %s" +msgstr "RSS %s" + +#: services/importer.js:249 +msgid "Could not access import file" +msgstr "Impossibile accedere al file da importare" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "Trigger %s sconosciuto" diff --git a/languages/pl_PL.mo b/languages/pl_PL.mo new file mode 100644 index 00000000..87fc07e4 Binary files /dev/null and b/languages/pl_PL.mo differ diff --git a/languages/pl_PL.po b/languages/pl_PL.po new file mode 100644 index 00000000..4e164b2f --- /dev/null +++ b/languages/pl_PL.po @@ -0,0 +1,3739 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-03-07 21:44+0200\n" +"PO-Revision-Date: 2019-05-09 14:02+0200\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.2.1\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 " +"|| n%100>14) ? 1 : 2);\n" +"Last-Translator: \n" +"Language: pl_PL\n" + +#: views/archive/layout.hbs:1 views/layout.hbs:1 +#: views/subscription/layout.hbs:1 routes/index.js:11 +msgid "Self hosted email newsletter app" +msgstr "Samodzielnie hostowana aplikacja e-mail newsletter" + +#: 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/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/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 "Strona główna" + +#: 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:119 routes/campaigns.js:35 +msgid "Campaigns" +msgstr "Kampanie" + +#: views/campaigns/bounced.hbs:3 views/campaigns/bounced.hbs:4 +msgid "Bounced info" +msgstr "Informacje o odrzuconych/odbiciach" + +#: 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 "Zobacz kampanię" + +#: views/campaigns/bounced.hbs:6 +msgid "Subscribers who bounced and were unsubscribed:" +msgstr "Subskrybenci, którzy odbili wiadomość i zrezygnowali z subskrypcji:" + +#: views/campaigns/bounced.hbs:7 views/campaigns/clicked.hbs:13 +#: views/campaigns/complained.hbs:7 views/campaigns/delivered.hbs:7 +#: views/campaigns/opened.hbs:7 views/campaigns/unsubscribed.hbs:7 +#: views/lists/subscription/import-failed.hbs:9 views/lists/view.hbs:18 +#: views/triggers/triggered.hbs:6 +msgid "Address" +msgstr "Adres" + +#: views/campaigns/bounced.hbs:8 views/campaigns/clicked.hbs:14 +#: views/campaigns/complained.hbs:8 views/campaigns/delivered.hbs:8 +#: views/campaigns/opened.hbs:8 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:19 +#: views/subscription/manage.hbs:4 views/subscription/subscribe.hbs:4 +#: views/triggers/triggered.hbs:7 +msgid "First Name" +msgstr "Imię" + +#: views/campaigns/bounced.hbs:9 views/campaigns/clicked.hbs:15 +#: views/campaigns/complained.hbs:9 views/campaigns/delivered.hbs:9 +#: views/campaigns/opened.hbs:9 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:20 +#: views/subscription/manage.hbs:5 views/subscription/subscribe.hbs:5 +#: views/triggers/triggered.hbs:8 +msgid "Last Name" +msgstr "Nazwisko" + +#: views/campaigns/bounced.hbs:10 views/campaigns/complained.hbs:10 +#: views/campaigns/delivered.hbs:10 views/campaigns/unsubscribed.hbs:10 +msgid "SMTP response" +msgstr "Odpowiedź SMTP" + +#: views/campaigns/bounced.hbs:11 +msgid "Bounce time" +msgstr "Czas odbicia" + +#: views/campaigns/campaigns.hbs:3 views/campaigns/create-triggered.hbs:24 +#: views/campaigns/create.hbs:3 views/campaigns/create.hbs:4 +#: views/campaigns/create.hbs:27 +msgid "Create Campaign" +msgstr "Utwórz Kampanię" + +#: views/campaigns/campaigns.hbs:4 +msgid "Regular Campaign" +msgstr "Zwykła kampania" + +#: views/campaigns/campaigns.hbs:5 +msgid "RSS Campaign" +msgstr "Kampania RSS" + +#: views/campaigns/campaigns.hbs:6 +msgid "Triggered Campaign" +msgstr "Wywołana kampania" + +#: 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:71 +#: views/lists/create.hbs:5 views/lists/edit.hbs:6 +#: views/lists/fields/fields.hbs:6 views/lists/lists.hbs:5 +#: views/lists/segments/segments.hbs:6 views/templates/templates.hbs:5 +#: views/triggers/triggers.hbs:5 +msgid "Name" +msgstr "Nazwa" + +#: 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:72 +#: views/lists/create.hbs:7 views/lists/edit.hbs:10 views/lists/lists.hbs:8 +#: views/partials/merge-tag-reference.hbs:4 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 "Opis" + +#: views/campaigns/campaigns.hbs:10 views/campaigns/view.hbs:73 +#: views/lists/view.hbs:21 views/lists/view.hbs:29 +#: views/triggers/triggers.hbs:6 +msgid "Status" +msgstr "Status" + +#: views/campaigns/campaigns.hbs:11 views/campaigns/view.hbs:74 +#: views/lists/view.hbs:22 views/lists/view.hbs:23 +msgid "Created" +msgstr "Utworzono" + +#: views/campaigns/clicked.hbs:3 views/campaigns/clicked.hbs:4 +msgid "Link info" +msgstr "Informacje o linkach" + +#: views/campaigns/clicked.hbs:6 views/campaigns/view.hbs:61 +msgid "URL" +msgstr "URL" + +#: views/campaigns/clicked.hbs:7 views/campaigns/view.hbs:62 +msgid "Clicks" +msgstr "Kliknięcia" + +#: views/campaigns/clicked.hbs:8 views/campaigns/view.hbs:63 +msgid "% of clicks" +msgstr "% kliknięć" + +#: views/campaigns/clicked.hbs:9 views/campaigns/view.hbs:64 +msgid "% of messages" +msgstr "% wiadomości" + +#: views/campaigns/clicked.hbs:10 views/campaigns/view.hbs:67 +msgid "Aggregated clicks" +msgstr "Zagregowane kliknięcia" + +#: views/campaigns/clicked.hbs:11 +msgid "Subscribers who clicked on a link:" +msgstr "Subskrybenci, którzy kliknęli link:" + +#: views/campaigns/clicked.hbs:12 +msgid "Subscribers who clicked on this link:" +msgstr "Subskrybenci, którzy kliknęli ten link:" + +#: views/campaigns/clicked.hbs:16 +msgid "First click time" +msgstr "Czas pierwszego kliknięcia" + +#: views/campaigns/clicked.hbs:17 +msgid "Click count" +msgstr "Liczba kliknięć" + +#: views/campaigns/complained.hbs:3 views/campaigns/complained.hbs:4 +msgid "Complained info" +msgstr "Informacje o skargach" + +#: views/campaigns/complained.hbs:6 +msgid "Subscribers who complained and were unsubscribed:" +msgstr "Subskrybenci, którzy złożyli skargę i wypisali się:" + +#: views/campaigns/complained.hbs:11 +msgid "Complain time" +msgstr "Czas skargi" + +#: views/campaigns/create-rss.hbs:3 views/campaigns/create-rss.hbs:4 +#: views/campaigns/create-rss.hbs:20 +msgid "Create RSS Campaign" +msgstr "Utwórz kampanię 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 "" +"Kampania RSS ustawia moduł śledzący na wybrany adres RSS. Za każdym razem, " +"gdy nowy wpis zostanie znaleziony z tego kanału, zostanie wysłany na wybraną " +"listę jako wiadomość e-mail." + +#: 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 "Nazwa kampanii" + +#: 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/templates/create.hbs:11 views/templates/edit.hbs:10 +#: views/triggers/create.hbs:9 views/triggers/edit.hbs:10 +msgid "HTML is allowed" +msgstr "HTML dozwolony" + +#: 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:13 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/subscription/manage.hbs:10 views/subscription/subscribe.hbs:10 +#: 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 "Wybierz" + +#: 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 "subskrybenci" + +#: views/campaigns/create-rss.hbs:13 views/campaigns/edit-rss.hbs:15 +msgid "RSS Feed Url" +msgstr "Adres URL kanału RSS" + +#: 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 "" +"Nowe wpisy z tego adresu RSS są wysyłane do listy subskrybentów, jak " +"wiadomości e-mail" + +#: views/campaigns/create-rss.hbs:15 views/campaigns/create-triggered.hbs:17 +#: 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 "E-mail nazwa \"from\"" + +#: views/campaigns/create-rss.hbs:16 views/campaigns/create-triggered.hbs:18 +#: views/campaigns/create.hbs:19 views/campaigns/edit-rss.hbs:19 +#: views/campaigns/edit.hbs:18 views/settings.hbs:23 +msgid "This is the name your emails will come from" +msgstr "This is the name your emails will come from" + +#: views/campaigns/create-rss.hbs:17 views/campaigns/create-triggered.hbs:19 +#: 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 "E-mail adres \"from\"" + +#: views/campaigns/create-rss.hbs:18 views/campaigns/create-triggered.hbs:20 +#: 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 "To jest adres na który ludzie będą wysyłać odpowiedzi" + +#: views/campaigns/create-rss.hbs:19 views/campaigns/create-triggered.hbs:23 +#: views/campaigns/create.hbs:26 views/campaigns/edit-rss.hbs:22 +#: views/campaigns/edit-triggered.hbs:22 views/campaigns/edit.hbs:25 +msgid "Disable clicked/opened tracking" +msgstr "Wyłącz kliknięte/otwarte śledzenie" + +#: views/campaigns/create-triggered.hbs:3 +#: views/campaigns/create-triggered.hbs:4 +msgid "Create Triggered Campaign" +msgstr "Utwórz uruchamianą kampanię" + +#: 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/templates/create.hbs:13 +msgid "Template" +msgstr "Szablon" + +#: views/campaigns/create-triggered.hbs:14 views/campaigns/create.hbs:15 +msgid "Selecting a template creates a campaign specific copy from it" +msgstr "Wybranie szablonu powoduje utworzenie z niego kopii dla kampanii" + +#: views/campaigns/create-triggered.hbs:15 views/campaigns/create.hbs:16 +msgid "Or alternatively use an URL as the message content source:" +msgstr "Lub alternatywnie użyj adresu URL jako źródło treści wiadomości:" + +#: views/campaigns/create-triggered.hbs:16 views/campaigns/create.hbs:17 +#: views/campaigns/edit-triggered.hbs:25 views/campaigns/edit.hbs:28 +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 "" +"Jeśli wiadomość zostanie wysłana, ten adres URL zostanie wysłany POST do " +"używania merge tagów jako treść POST. Użyj tej opcji, jeśli chcesz " +"samodzielnie wygenerować wiadomość HTML" + +#: views/campaigns/create-triggered.hbs:21 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 "E-mail temat \"from\"" + +#: views/campaigns/create-triggered.hbs:22 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 "Zachowaj to, co istotne i nie spamuj" + +#: views/campaigns/create.hbs:13 +msgid "Select a template:" +msgstr "Wybierz szablon:" + +#: 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 "" +"To jest adres, na który ludzie będą wysyłać odpowiedzi, chyba że ustawiono " +"adres zwrotny" + +#: views/campaigns/create.hbs:22 views/campaigns/edit.hbs:21 +#: views/campaigns/view.hbs:14 +msgid "Email \"reply-to\" address" +msgstr "Email \"Odpowiedz do\"" + +#: views/campaigns/create.hbs:23 views/campaigns/edit.hbs:22 +msgid "If set, this is the address people will send replies to" +msgstr "" +"Jeśli jest ustawiony, jest to adres, na który ludzie będą wysyłać odpowiedzi" + +#: views/campaigns/delivered.hbs:3 views/campaigns/delivered.hbs:4 +msgid "Delivered info" +msgstr "Informacje o dostarczonych" + +#: views/campaigns/delivered.hbs:6 +msgid "Subscribers who received the message and did not bounce/unsubscribe:" +msgstr "" +"Subskrybenci, którzy otrzymali wiadomość i nie odrzucili/nie anulowali " +"subskrypcji:" + +#: views/campaigns/delivered.hbs:11 +msgid "Delivery time" +msgstr "Czas dostarczenia" + +#: views/campaigns/edit-rss.hbs:3 views/campaigns/edit-rss.hbs:4 +msgid "Edit RSS Campaign" +msgstr "Edytuj kampanię 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 "Ustawienia główne" + +#: 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 "" +"Użyj specjalnego merge tagu [RSS_ENTRY], aby oznaczyć pozycję treści " +"wiadomości RSS. Dodatkowo mżesz użyć dowolnego innego merge tagu." + +#: views/campaigns/edit-rss.hbs:23 views/campaigns/edit-triggered.hbs:26 +#: views/campaigns/edit.hbs:34 +msgid "Delete Campaign" +msgstr "Usuń kampanię" + +#: views/campaigns/edit-rss.hbs:24 views/campaigns/edit-triggered.hbs:27 +#: views/campaigns/edit.hbs:35 views/lists/edit.hbs:13 +#: views/lists/fields/edit.hbs:39 views/lists/segments/edit.hbs:14 +#: views/lists/segments/rule-edit.hbs:38 views/lists/subscription/edit.hbs:17 +#: views/settings.hbs:99 views/templates/edit.hbs:12 views/triggers/edit.hbs:30 +#: views/users/account.hbs:18 +msgid "Update" +msgstr "Zaktualizuj" + +#: views/campaigns/edit-triggered.hbs:3 views/campaigns/edit-triggered.hbs:4 +msgid "Edit Triggered Campaign" +msgstr "Edytuj kampanię uruchamianą" + +#: views/campaigns/edit-triggered.hbs:6 views/campaigns/edit.hbs:6 +msgid "General" +msgstr "Główne" + +#: views/campaigns/edit-triggered.hbs:17 +msgid "his is the name your emails will come from" +msgstr "Nazwa, z której będą wysyłane wszystkie e-maile." + +#: views/campaigns/edit-triggered.hbs:23 views/campaigns/edit.hbs:26 +msgid "Template Settings" +msgstr "Ustawienia szablonu" + +#: views/campaigns/edit-triggered.hbs:24 views/campaigns/edit.hbs:27 +msgid "Template URL" +msgstr "Adres URL szablonu" + +#: 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 "Edytuj kampanie" + +#: views/campaigns/edit.hbs:8 views/campaigns/edit.hbs:29 +msgid "Attachments" +msgstr "Załączniki" + +#: views/campaigns/edit.hbs:30 +msgid "File" +msgstr "Plik" + +#: views/campaigns/edit.hbs:31 +msgid "Size" +msgstr "Rozmiar" + +#: views/campaigns/edit.hbs:32 views/campaigns/view.hbs:66 +#: views/lists/fields/fields.hbs:12 views/lists/view.hbs:32 +msgid "No data available in table" +msgstr "Brak danych w tabeli" + +#: views/campaigns/edit.hbs:33 views/campaigns/upload-attachment.hbs:4 +msgid "Add Attachment" +msgstr "Dodaj załącznik" + +#: views/campaigns/opened.hbs:3 views/campaigns/opened.hbs:4 +msgid "Opened info" +msgstr "Informacje o otwarciach" + +#: views/campaigns/opened.hbs:6 +msgid "Subscribers who opened this message:" +msgstr "Subskrybenci, którzy otworzyli tę wiadomość:" + +#: views/campaigns/opened.hbs:10 +msgid "First open" +msgstr "Pierwsze otwarcie" + +#: views/campaigns/opened.hbs:11 +msgid "Opened count" +msgstr "Liczba otwarć" + +#: views/campaigns/unsubscribed.hbs:3 views/campaigns/unsubscribed.hbs:4 +msgid "Unsubscribed info" +msgstr "Informacje o anulowaniach subskrypcji" + +#: views/campaigns/unsubscribed.hbs:6 +msgid "Subscribers who unsubscribed:" +msgstr "Subskrybenci, którzy anulowali subskrypcję:" + +#: views/campaigns/unsubscribed.hbs:11 views/campaigns/view.hbs:26 +#: views/lists/subscription/import.hbs:10 routes/lists.js:171 +msgid "Unsubscribed" +msgstr "Anulowano subskrypcję" + +#: views/campaigns/upload-attachment.hbs:7 +msgid "Upload" +msgstr "Prześlij" + +#: views/campaigns/view.hbs:4 +msgid "Overview" +msgstr "Przegląd" + +#: views/campaigns/view.hbs:5 +msgid "Links" +msgstr "Linków" + +#: views/campaigns/view.hbs:7 +msgid "Feed URL" +msgstr "Url czytnika" + +#: views/campaigns/view.hbs:8 +msgid "Last check" +msgstr "Ostatnie sprawdzenie" + +#: views/campaigns/view.hbs:9 +msgid "Not yet checked" +msgstr "Jeszcze nie sprawdzone" + +#: views/campaigns/view.hbs:10 +msgid "activate campaign to start checking feed for new messages" +msgstr "" +"aktywuj kampanię, aby rozpocząć sprawdzanie kanału dla nowych wiadomości" + +#: views/campaigns/view.hbs:11 +msgid "RSS status" +msgstr "Status RSS" + +#: views/campaigns/view.hbs:16 +msgid "Preview campaign as" +msgstr "Podgląd kampanii jako" + +#: views/campaigns/view.hbs:17 +msgid "Add new test user" +msgstr "Dodaj użytkownika testowego" + +#: views/campaigns/view.hbs:18 +msgid "No test users yet, create one here" +msgstr "Brak użytkowników testowych, utwórz je tutaj" + +#: views/campaigns/view.hbs:19 +msgid "Go" +msgstr "Dalej" + +#: views/campaigns/view.hbs:20 lib/models/triggers.js:25 +msgid "Delivered" +msgstr "Dostarczone" + +#: views/campaigns/view.hbs:21 +msgid "List subscribers who received this message" +msgstr "Lista subskrybentów, którzy otrzymali tę wiadomość" + +#: views/campaigns/view.hbs:22 routes/lists.js:171 +msgid "Bounced" +msgstr "Odbite" + +#: views/campaigns/view.hbs:23 +msgid "List subscribers who bounced" +msgstr "Lista użytkowników którzy odbili" + +#: views/campaigns/view.hbs:24 +msgid "Complaints" +msgstr "Skargi" + +#: views/campaigns/view.hbs:25 +msgid "List subscribers who complained for this message" +msgstr "Lista subskrybentów, którzy złożyli skargę na tę wiadomość" + +#: views/campaigns/view.hbs:27 +msgid "List subscribers who unsubscribed after this message" +msgstr "Lista subskrybentów, którzy anulowali subskrypcję po tej wiadomości" + +#: views/campaigns/view.hbs:28 +msgid "Opened" +msgstr "Otwarto" + +#: views/campaigns/view.hbs:29 +msgid "List subscribers who opened this message" +msgstr "Lista subskrybentów, którzy otworzyli tę wiadomość" + +#: views/campaigns/view.hbs:30 +msgid "Clicked" +msgstr "Kliknięto" + +#: views/campaigns/view.hbs:31 views/campaigns/view.hbs:68 +msgid "List subscribers who clicked on a link" +msgstr "Lista subskrybentów, którzy kliknęli w link" + +#: views/campaigns/view.hbs:32 +msgid "" +"Are you sure? This action would start sending messages to the selected list" +msgstr "" +"Jesteś pewny? Ta czynność rozpocznie wysyłanie wiadomości do wybranej listy" + +#: views/campaigns/view.hbs:33 +msgid "Delay sending" +msgstr "Opóźnienie wysyłania" + +#: views/campaigns/view.hbs:34 +msgid "hours" +msgstr "godziny" + +#: views/campaigns/view.hbs:35 +msgid "minutes" +msgstr "minuty" + +#: views/campaigns/view.hbs:36 +msgid "Send to subscribers:" +msgstr "Wyślij do subskrybentów:" + +#: views/campaigns/view.hbs:37 +msgid "Are you sure? This action would reset scheduling" +msgstr "Jesteś pewny? Ta czynność zresetuje harmonogram" + +#: views/campaigns/view.hbs:38 +msgid "Cancel" +msgstr "Anuluj" + +#: views/campaigns/view.hbs:39 +msgid "Sending scheduled" +msgstr "Wysyłanie harmonogramu" + +#: views/campaigns/view.hbs:40 views/campaigns/view.hbs:52 +msgid "Pause" +msgstr "Pauza" + +#: views/campaigns/view.hbs:41 routes/campaigns.js:264 +msgid "Sending" +msgstr "Wysyłanie" + +#: views/campaigns/view.hbs:42 views/campaigns/view.hbs:46 +msgid "" +"Are you sure? This action would resume sending messages to the selected list" +msgstr "" +"Jesteś pewny? Ta czynność wznowi wysyłanie wiadomości do wybranej listy" + +#: views/campaigns/view.hbs:43 views/campaigns/view.hbs:47 +msgid "Are you sure? This action would reset all stats about current progress" +msgstr "" +"Jesteś pewny? Ta czynność zresetuje wszystkie statystyki dotyczące bieżącego " +"postępu" + +#: views/campaigns/view.hbs:44 +msgid "Resume" +msgstr "Wznów" + +#: views/campaigns/view.hbs:45 views/campaigns/view.hbs:49 +msgid "Reset" +msgstr "Resetuj" + +#: views/campaigns/view.hbs:48 +msgid "Continue" +msgstr "Kontynuuj" + +#: views/campaigns/view.hbs:50 +msgid "" +"All messages sent! Hit \"Continue\" if you you want to send this campaign to " +"new subscribers" +msgstr "" +"Wszystkie wiadomości wysłane! Kliknij \"Kontynuuj\", jeśli chcesz wysłać tę " +"kampanię do nowych subskrybentów" + +#: views/campaigns/view.hbs:51 +msgid "" +"Are you sure? This action would pause sending new entries in RSS feed as " +"email messages to the selected list" +msgstr "" +"Jesteś pewny? Ta czynność wstrzyma wysyłanie wiadomości o nowych wpisach w " +"kanale RSS do wybranej listy" + +#: views/campaigns/view.hbs:53 views/campaigns/view.hbs:57 +msgid "Campaign status:" +msgstr "Status kampanii:" + +#: views/campaigns/view.hbs:54 +msgid "ACTIVE" +msgstr "AKTYWNE" + +#: views/campaigns/view.hbs:55 +msgid "" +"Are you sure? This action would start sending new entries in RSS feed as " +"email messages to the selected list" +msgstr "" +"Jesteś pewny? Ta czynność rozpocznie wysyłanie wiadomości o nowych wpisach w " +"kanale RSS do wybranej listy" + +#: views/campaigns/view.hbs:56 +msgid "Activate" +msgstr "Aktywuj" + +#: views/campaigns/view.hbs:58 +msgid "INACTIVE" +msgstr "NIEAKTYWNE" + +#: views/campaigns/view.hbs:59 +msgid "" +"This is a triggered campaign. Messages are only sent to subscribers that hit " +"some trigger that invokes this campaign" +msgstr "" +"To jest kampania wyzwalana. Wiadomości są wysyłane tylko do subskrybentów, " +"którzy natrafili na jakiś wyzwalacz, który wywołuje tę kampanię" + +#: views/campaigns/view.hbs:60 +msgid "see more" +msgstr "zobacz więcej" + +#: views/campaigns/view.hbs:65 +msgid "List subscribers who clicked this link" +msgstr "Lista subskrybentów, którzy kliknęli link" + +#: views/campaigns/view.hbs:69 +msgid "" +"Clicks are counted as unique subscribers that clicked on a specific link or " +"on any link (in aggregated view)" +msgstr "" +"Kliknięcia są liczone jako unikalni subskrybenci, którzy kliknęli określony " +"link lub dowolny link (w widoku zbiorczym)" + +#: views/campaigns/view.hbs:70 +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 "" +"Jeśli zostanie znaleziony nowy wpis z kanału kampanii, zostanie utworzona " +"nowa kampania podrzędna dla tego wpisu i zostanie ona tutaj wymieniona" + +#: views/emails/confirm-html.hbs:1 views/emails/confirm-html.hbs:2 +#: views/emails/confirm-text.hbs:1 +msgid "Please Confirm Subscription" +msgstr "Proszę potwierdź subskrypcję" + +#: views/emails/confirm-html.hbs:3 views/emails/confirm-text.hbs:2 +msgid "Yes, subscribe me to this list" +msgstr "Tak, zapisz mnie na listę" + +#: views/emails/confirm-html.hbs:4 +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 "" +"Jeśli przez pomyłkę otrzymałeś tego e-maila, po prostu go usuń. Nie będziesz " +"subskrybowany, jeśli nie klikniesz w link potwierdzający." + +#: views/emails/confirm-html.hbs:5 views/emails/confirm-text.hbs:4 +#: views/emails/subscription-confirmed-html.hbs:7 +#: views/emails/subscription-confirmed-text.hbs:7 +#: views/emails/unsubscribe-confirmed-html.hbs:5 +#: views/emails/unsubscribe-confirmed-text.hbs:5 +msgid "For questions about this list, please contact:" +msgstr "W przypadku pytań dotyczących tej listy, prosimy o kontakt:" + +#: views/emails/confirm-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 "" +"Jeśli przez pomyłkę otrzymałeś tego e-maila, po prostu go usuń. Nie będziesz " +"subskrybowany, chyba że klikniesz link powyżej." + +#: views/emails/password-reset-html.hbs:1 +#: views/emails/password-reset-html.hbs:2 +#: views/emails/password-reset-text.hbs:1 +msgid "Change your password" +msgstr "Zmień hasło" + +#: views/emails/password-reset-html.hbs:3 +#: views/emails/password-reset-text.hbs:2 +msgid "We have received a password change request for your Mailtrain account:" +msgstr "Otrzymaliśmy prośbę o zmianę hasła dla Twojego konta Mailtrain:" + +#: views/emails/password-reset-html.hbs:4 +#: views/emails/password-reset-text.hbs:3 +msgid "Reset password" +msgstr "Zresetuj hasło" + +#: views/emails/password-reset-html.hbs:5 +#: 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 "" +"Jeśli nie poprosiłeś o zmianę hasła, możesz zignorować ten e-mail, a Twoje " +"hasło nie zostanie zmienione." + +#: views/emails/rss-html.hbs:1 views/emails/stationery-html.hbs:3 +#: views/emails/stationery-text.hbs:3 +msgid "Preferences" +msgstr "Preferencje" + +#: views/emails/rss-html.hbs:2 views/emails/stationery-html.hbs:4 +#: views/emails/stationery-text.hbs:4 views/lists/subscription/edit.hbs:15 +#: views/subscription/manage.hbs:12 views/subscription/unsubscribe.hbs:1 +#: views/subscription/unsubscribe.hbs:4 routes/lists.js:253 +msgid "Unsubscribe" +msgstr "Anuluj subskrypcję" + +#: 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 "Zobacz ten email w Twojej przeglądarce" + +#: views/emails/stationery-html.hbs:1 views/emails/stationery-text.hbs:1 +msgid "Hey [FIRST_NAME/Customer]," +msgstr "Cześć [FIRST_NAME/Customer]," + +#: views/emails/stationery-html.hbs:2 views/emails/stationery-text.hbs:2 +msgid "Cheers," +msgstr "Pozdrowienia," + +#: views/emails/subscription-confirmed-html.hbs:1 +#: views/emails/subscription-confirmed-text.hbs:1 +#: views/subscription/subscribed.hbs:1 +msgid "Subscription Confirmed" +msgstr "Subskrypcja została potwierdzona" + +#: views/emails/subscription-confirmed-html.hbs:2 +#: views/emails/subscription-confirmed-text.hbs:2 +#: views/subscription/subscribed.hbs:2 +msgid "Your subscription to our list has been confirmed." +msgstr "Twoja subskrypcja naszej listy została potwierdzona." + +#: views/emails/subscription-confirmed-html.hbs:3 +#: views/emails/subscription-confirmed-text.hbs:3 +msgid "If you want to modify your subscription then you can:" +msgstr "Jeśli chcesz zmodyfikować swoją subskrypcję, możesz:" + +#: views/emails/subscription-confirmed-html.hbs:4 +#: views/emails/subscription-confirmed-text.hbs:4 +#: views/subscription/subscribed.hbs:6 +msgid "manage your preferences" +msgstr "zarządzaj swoimi preferencjami" + +#: views/emails/subscription-confirmed-html.hbs:5 +#: views/emails/subscription-confirmed-text.hbs:5 +#: views/subscription/subscribed.hbs:5 views/users/login.hbs:10 +msgid "or" +msgstr "lub" + +#: views/emails/subscription-confirmed-html.hbs:6 +#: views/emails/subscription-confirmed-text.hbs:6 +msgid "unsubscribe here" +msgstr "anuluj subskrypcję tutaj" + +#: views/emails/unsubscribe-confirmed-html.hbs:1 +#: views/emails/unsubscribe-confirmed-text.hbs:1 +msgid "You are now unsubscribed" +msgstr "Anulowałeś subskrypcję" + +#: views/emails/unsubscribe-confirmed-html.hbs:2 +#: views/emails/unsubscribe-confirmed-text.hbs:2 +msgid "We have removed your email address from our list." +msgstr "Usunęliśmy Twój adres e-mail z naszej listy." + +#: views/emails/unsubscribe-confirmed-html.hbs:3 +#: views/emails/unsubscribe-confirmed-text.hbs:3 +msgid "If you unsubscribed by mistake, you can re-subscribe at:" +msgstr "" +"Jeśli anulujesz subskrypcję przez pomyłkę, możesz ponownie zasubskrybować na:" + +#: views/emails/unsubscribe-confirmed-html.hbs:4 +#: views/emails/unsubscribe-confirmed-text.hbs:4 +#: views/lists/subscription/add.hbs:16 routes/lists.js:253 +msgid "Subscribe" +msgstr "Subskrybuj" + +#: views/index.hbs:1 +msgid "Official Mailtrain Partners" +msgstr "Oficjalni partnerzy Mailtrain" + +#: views/index.hbs:2 +msgid "Free, open source mail server solution" +msgstr "Darmowe, o otwartym kodzie źródłowym rozwiązanie serwera poczty" + +#: views/index.hbs:3 +msgid "" +"A reliable SMTP server, easy integration, and 12,000 messages a month free" +msgstr "" +"Niezawodny serwer SMTP, łatwa integracja i 12 000 wiadomości miesięcznie za " +"darmo" + +#: views/index.hbs:4 +msgid "List management" +msgstr "Zarządzanie listamie" + +#: views/index.hbs:5 +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 pozwala w łatwy sposób zarządzać nawet bardzo dużymi listami. " +"Milion subskrybentów? Żaden problem. Możesz dodawać subskrybentów ręcznie, " +"za pośrednictwem interfejsu API lub importować z pliku CSV. Wszystkie listy " +"zawierają również obsługę pól niestandardowych i znaczników scalających." + +#: views/index.hbs:6 +msgid "Custom fields" +msgstr "Pola niestandardowe" + +#: views/index.hbs:7 +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 "" +"Pola tekstowe, cyfry, listy rozwijane lub pola wyboru, Mailtrain ma je " +"wszystkie. Każde niestandardowe pole może być zawarte w wygenerowanych " +"newsletterach za pomocą merge tagów." + +#: views/index.hbs:8 +msgid "List segmentation" +msgstr "Segmentacja listy" + +#: 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 "" +"Wysyłaj wiadomości tylko dla subskrybentów, którzy pasują do " +"predefiniowanych reguł segmentacji. Nie trzeba tworzyć oddzielnych list z " +"małymi różnicami." + +#: views/index.hbs:10 +msgid "Donate to author" +msgstr "Przekaż darowiznę autorowi" + +#: views/index.hbs:11 +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 15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM" +msgstr "" +"Jeśli naprawdę lubisz Mailtrain lub Twoja firma czerpie z teg korzyści " +"finansowe będę naprawdę wdzięczny za niewielką darowiznę, aby utrzymać " +"rozwój Mailtrain. Możesz użyć Bitcoin lub PayPal do darowizn. Mój portfel " +"Bitcoin to 15Z8ADxhssKUiwP3jbbqJwA21744KMCfTM" + +#: views/index.hbs:12 +msgid "or donate using PayPal" +msgstr "lub przekaż darowiznę za pomocą PayPal" + +#: views/index.hbs:13 +msgid "RSS Campaigns" +msgstr "Kampanie RSS" + +#: views/index.hbs:14 +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 "" +"Przygotuj Mailtrain do śledzenia kanałów RSS i jeśli wykryty zostanie nowy " +"wpis w kanale, Mailtrain automatycznie generuje nową kampanię, wykorzystując " +"dane wejściowe jako treść wiadomości i wysyła je do wybranych subskrybentów." + +#: views/index.hbs:15 +msgid "GPG Encryption" +msgstr "Szyfrowanie GPG" + +#: views/index.hbs:16 +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 "" +"Jeśli lista ma niestandardowe pole dla zestawu kluczy publicznych GPG, " +"subskrybenci mogą przesłać swój klucz publiczny GPG, aby otrzymywać " +"zaszyfrowane wiadomości z listy." + +#: views/index.hbs:17 +msgid "Click stats" +msgstr "Statystyki kliknięć" + +#: views/index.hbs:18 +msgid "" +"After a campaign is sent, check individual click statistics for every link " +"included in the message." +msgstr "" +"Po wysłaniu kampanii sprawdź statystyki poszczególnych kliknięć dla każdego " +"linku zawartego w wiadomości." + +#: views/index.hbs:19 +msgid "Open source" +msgstr "Open source" + +#: views/index.hbs:20 +msgid "Mailtrain is available under GPLv3 license and completely open source." +msgstr "Mailtrain jest dostępny na licencji GPLv3 i całkowicie open source." + +#: views/index.hbs:21 +msgid "Send via any provider" +msgstr "Wyślij przez dowolnego dostawcę" + +#: views/index.hbs:22 +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 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." +"Odbieranie i obsługa skarg za pośrednictwem webhooków jest obsługiwane dla " +"SES, SparkPost, SendGrid i Mailgun, także dla Postfix i ZoneMTA." + +#: views/index.hbs:23 +msgid "Trigger based automation" +msgstr "Automatyzacja oparta na wyzwalaczach" + +#: views/index.hbs:24 +msgid "" +"Define automation triggers to send specific messages when a user activates " +"the trigger." +msgstr "" +"Zdefiniuj wyzwalacze automatyzacji, aby wysłać określone komunikaty, gdy " +"użytkownik aktywuje wyzwalacz." + +#: views/layout.hbs:2 +msgid "Toggle navigation" +msgstr "Przełącz nawigację" + +#: 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 "Konto" + +#: views/layout.hbs:6 views/settings.hbs:2 views/settings.hbs:3 +msgid "Settings" +msgstr "Ustawienia" + +#: views/layout.hbs:7 views/users/api.hbs:2 views/users/api.hbs:3 +msgid "API" +msgstr "API" + +#: views/layout.hbs:8 +msgid "Log out" +msgstr "Wyloguj" + +#: views/layout.hbs:9 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 "Zaloguj się" + +#: views/layout.hbs:10 +msgid "Self hosted newsletter app built on top of Nodemailer" +msgstr "" +"Samodzielnie hostowana aplikacja e-mail newsletter zbudowana na bazie " +"Nodemailera" + +#: views/layout.hbs:11 views/layout.hbs:13 +msgid "Source on GitHub" +msgstr "Źródło na GitHub" + +#: views/layout.hbs:12 +msgid "Subscribe to our newsletter" +msgstr "Zapisz się do naszego newslettera" + +#: 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/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:111 +msgid "Lists" +msgstr "Listy" + +#: views/lists/create.hbs:3 views/lists/create.hbs:4 views/lists/create.hbs:9 +#: views/lists/lists.hbs:3 +msgid "Create List" +msgstr "Utwórz listę" + +#: views/lists/create.hbs:6 views/lists/edit.hbs:7 +msgid "List Name" +msgstr "Nazwa listy" + +#: views/lists/edit.hbs:3 views/lists/edit.hbs:4 views/lists/view.hbs:7 +msgid "Edit List" +msgstr "Edytuj listę" + +#: views/lists/edit.hbs:5 +msgid "View List" +msgstr "Wyświetl listę" + +#: views/lists/edit.hbs:8 +msgid "List ID" +msgstr "ID listy" + +#: views/lists/edit.hbs:9 +msgid "This is the list ID displayed to the subscribers" +msgstr "To jest ID listy wyświetlany dla subskrybentów" + +#: views/lists/edit.hbs:12 +msgid "Delete List" +msgstr "Usuń listę" + +#: 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:5 +msgid "Custom Fields" +msgstr "Pola niestandardowe" + +#: views/lists/fields/create.hbs:4 +msgid "Create Field" +msgstr "Utwórz pole" + +#: views/lists/fields/create.hbs:5 views/lists/fields/fields.hbs:4 +msgid "Create Custom Field" +msgstr "Utwórz pole niestandardowe" + +#: 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 "Nazwa pola" + +#: views/lists/fields/create.hbs:8 views/lists/fields/edit.hbs:9 +msgid "Field Type" +msgstr "Typ pola" + +#: views/lists/fields/create.hbs:9 views/lists/fields/edit.hbs:10 +#: lib/models/fields.js:17 +msgid "Text" +msgstr "Tekst" + +#: views/lists/fields/create.hbs:10 views/lists/fields/edit.hbs:11 +#: lib/models/fields.js:21 +msgid "Number" +msgstr "Numer" + +#: views/lists/fields/create.hbs:11 views/lists/fields/edit.hbs:12 +#: lib/models/fields.js:18 +msgid "Website" +msgstr "Strona www" + +#: views/lists/fields/create.hbs:12 views/lists/fields/edit.hbs:13 +#: lib/models/fields.js:20 +msgid "GPG Public Key" +msgstr "Klucz publiczny GPG" + +#: views/lists/fields/create.hbs:13 views/lists/fields/edit.hbs:14 +#: lib/models/fields.js:19 +msgid "Multi-line text" +msgstr "Tekst wielowierszowy" + +#: 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 "Data" + +#: views/lists/fields/create.hbs:16 views/lists/fields/edit.hbs:17 +msgid "Date (MM/DD/YYYY)" +msgstr "Data (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 "Data (DD/MM/YYYY)" + +#: views/lists/fields/create.hbs:18 views/lists/fields/edit.hbs:19 +msgid "Birthday" +msgstr "Urodziny" + +#: views/lists/fields/create.hbs:19 views/lists/fields/edit.hbs:20 +#: lib/models/fields.js:27 +msgid "Birthday (MM/DD)" +msgstr "Urodziny (MM/DD)" + +#: views/lists/fields/create.hbs:20 views/lists/fields/edit.hbs:21 +#: lib/models/fields.js:28 +msgid "Birthday (DD/MM)" +msgstr "Urodziny (DD/MM)" + +#: views/lists/fields/create.hbs:21 views/lists/fields/edit.hbs:22 +msgid "Grouped" +msgstr "Grouped." + +#: views/lists/fields/create.hbs:22 views/lists/fields/edit.hbs:23 +msgid "Drop Downs" +msgstr "Listy rozwijane" + +#: views/lists/fields/create.hbs:23 views/lists/fields/edit.hbs:24 +#: lib/models/fields.js:22 +msgid "Radio Buttons" +msgstr "Radio Buttons" + +#: 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 "Opcja dla wartości grupy" + +#: views/lists/fields/create.hbs:26 views/lists/fields/edit.hbs:27 +msgid "Group" +msgstr "Grupa" + +#: views/lists/fields/create.hbs:28 views/lists/fields/edit.hbs:29 +msgid "Required for group options" +msgstr "Wymagane w przypadku opcji grupowych" + +#: 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 "Domyślna wartość merge tag" + +#: 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 "" +"W przypadku elementów grupy, takich jak checkboxy, można kontrolować wygląd " +"merge tagów za pomocą opcjonalnego szablonu. Szablon używa składni " +"handlebars i możesz znaleźć wszystkie wartości z tablicy {{values}}, na przykład {{#each values}} {{this}} {{/each}} . Jeśli " +"szablon nie jest zdefiniowany, wiele wartości jest łączonych przecinkami. " +"Możesz również użyć tego szablonu do renderowania wartości JSON (jeśli JSON " +"jest tablicą, wtedy tablica jest wyświetlana jako values, w " +"przeciwnym razie możesz uzyskać bezpośredni dostęp do kluczy JSON)." + +#: views/lists/fields/create.hbs:33 views/lists/fields/edit.hbs:37 +msgid "Visible" +msgstr "Widoczny" + +#: views/lists/fields/create.hbs:34 +msgid "Add Field" +msgstr "Dodaj pole" + +#: views/lists/fields/edit.hbs:4 +msgid "Edit Field" +msgstr "Edytuj pole" + +#: views/lists/fields/edit.hbs:5 +msgid "Edit Custom Field" +msgstr "Edytuj pole niestandardowe" + +#: views/lists/fields/edit.hbs:6 +msgid "Back to fields" +msgstr "Powrót do pól" + +#: views/lists/fields/edit.hbs:30 views/lists/fields/fields.hbs:8 +#: views/partials/merge-tag-reference.hbs:3 +msgid "Merge tag" +msgstr "Merge tag" + +#: views/lists/fields/edit.hbs:31 +msgid "Merge Tag" +msgstr "Merge Tag" + +#: views/lists/fields/edit.hbs:32 +msgid "Put this tag in your content:" +msgstr "Umieść ten tag w swojej treści:" + +#: views/lists/fields/edit.hbs:38 +msgid "Delete Field" +msgstr "Usuń pole" + +#: views/lists/fields/fields.hbs:7 views/lists/view.hbs:25 +msgid "Type" +msgstr "Typ" + +#: views/lists/fields/fields.hbs:10 views/lists/fields/fields.hbs:11 +#: views/lists/lists.hbs:9 views/lists/segments/segments.hbs:8 +#: views/lists/segments/view.hbs:12 views/templates/templates.hbs:7 +#: views/triggers/triggers.hbs:14 routes/campaigns.js:287 +#: routes/campaigns.js:576 routes/campaigns.js:626 routes/lists.js:222 +#: routes/triggers.js:297 +msgid "Edit" +msgstr "Edytuj" + +#: views/lists/lists.hbs:6 +msgid "ID" +msgstr "ID" + +#: views/lists/lists.hbs:7 +msgid "Subscribers" +msgstr "Subskrybenci" + +#: 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:6 +#: views/lists/view.hbs:13 +msgid "Segments" +msgstr "Segmenty" + +#: 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 "Utwórz segment" + +#: 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 "Nazwa segmentu" + +#: views/lists/segments/create.hbs:8 views/lists/segments/edit.hbs:9 +msgid "Rule match" +msgstr "Dopasowanie reguł" + +#: views/lists/segments/create.hbs:10 views/lists/segments/edit.hbs:11 +msgid "All rules must match" +msgstr "Wszystkie reguły muszą się zgadzać" + +#: views/lists/segments/create.hbs:11 views/lists/segments/edit.hbs:12 +msgid "Any rule can match" +msgstr "Każda reguła może się zgadzać" + +#: views/lists/segments/create.hbs:12 +msgid "Add Segment" +msgstr "Dodaj segment" + +#: views/lists/segments/edit.hbs:4 views/lists/segments/edit.hbs:5 +#: views/lists/segments/view.hbs:6 views/lists/view.hbs:11 +msgid "Edit Segment" +msgstr "Edytuj segment" + +#: views/lists/segments/edit.hbs:6 +msgid "Back to segments" +msgstr "Powrót do segmentów" + +#: views/lists/segments/edit.hbs:13 +msgid "Delete Segment" +msgstr "Usuń segment" + +#: 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 "Utwórz regułę" + +#: 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 "Reguła" + +#: 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 "Wartość" + +#: 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 "" +"Użyj % dla znaku wieloznacznego, np. \"%test\", aby dopasować wszystkie " +"wartości, które kończą się na \"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 "Użyj ścisłego dopasowania" + +#: 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 "Użyj dopasowania zakresu" + +#: views/lists/segments/rule-configure.hbs:16 +#: views/lists/segments/rule-edit.hbs:20 +msgid "Use relative range match" +msgstr "Użyj względnego dopasowania zakresu" + +#: 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 "Od" + +#: 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 "dni" + +#: 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 "przed dzisiaj" + +#: 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 "po dzisiaj" + +#: 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 "do" + +#: 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 "Wybrano" + +#: 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 "Nie wybrano" + +#: views/lists/segments/rule-configure.hbs:33 +msgid "Add Rule" +msgstr "Dodaj regułę" + +#: views/lists/segments/rule-create.hbs:8 +#: views/lists/subscription/import.hbs:12 views/triggers/create-select.hbs:9 +msgid "Next" +msgstr "Dalej" + +#: views/lists/segments/rule-edit.hbs:37 +msgid "Delete Rule" +msgstr "Usuń regułę" + +#: views/lists/segments/segments.hbs:7 +msgid "Match" +msgstr "Dopasowanie" + +#: views/lists/segments/view.hbs:5 views/lists/view.hbs:12 +msgid "Segment" +msgstr "Segment" + +#: views/lists/segments/view.hbs:7 +msgid "Match rules" +msgstr "Reguły dopasowania" + +#: views/lists/segments/view.hbs:8 +msgid "Matching subscribers" +msgstr "Dopasowani subskrybenci" + +#: views/lists/segments/view.hbs:9 +msgid "show" +msgstr "pokaż" + +#: views/lists/subscription/add.hbs:3 views/lists/subscription/add.hbs:4 +msgid "Add subscriber" +msgstr "Dodaj subskrybenta" + +#: views/lists/subscription/add.hbs:5 views/subscription/manage.hbs:2 +#: views/subscription/subscribe.hbs:3 views/users/account.hbs:7 +msgid "Email Address" +msgstr "Adres e-mail" + +#: views/lists/subscription/add.hbs:8 views/lists/subscription/edit.hbs:9 +#: views/settings.hbs:82 views/settings.hbs:97 views/subscription/manage.hbs:7 +#: views/subscription/subscribe.hbs:7 +msgid "Begins with" +msgstr "Zaczyna się" + +#: 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 "" +"Wstaw klucz publiczny GPG, który będzie używany do szyfrowania wiadomości " +"wysłanych przez tego subskrybenta" + +#: views/lists/subscription/add.hbs:11 views/lists/subscription/edit.hbs:12 +#: views/lists/subscription/import-preview.hbs:9 +msgid "Timezone" +msgstr "Strefa czasowa" + +#: views/lists/subscription/add.hbs:13 views/lists/subscription/edit.hbs:13 +msgid "Test user?" +msgstr "Użytkownik testowy?" + +#: 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 "" +"Jeśli ta opcja jest zaznaczona, można użyć tej subskrypcji do przeglądania " +"wiadomości kampanii" + +#: 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 "" +"Ta osoba nie otrzyma e-maila z potwierdzeniem, więc upewnij się, że masz " +"uprawnienia do e-maila." + +#: views/lists/subscription/edit.hbs:3 views/lists/subscription/edit.hbs:4 +msgid "Edit subscriber" +msgstr "Edytuj subskrybenta" + +#: views/lists/subscription/edit.hbs:5 +#: views/lists/subscription/import-failed.hbs:5 +msgid "Back to list" +msgstr "Powrót do listy" + +#: views/lists/subscription/edit.hbs:6 +#: views/lists/subscription/import-preview.hbs:6 +#: views/subscription/unsubscribe.hbs:3 lib/helpers.js:26 +#: lib/models/segments.js:11 +msgid "Email address" +msgstr "Adres e-mail" + +#: views/lists/subscription/edit.hbs:16 +msgid "Delete Subscription" +msgstr "Usuń subskrypcję" + +#: views/lists/subscription/import-failed.hbs:3 +msgid "Import status" +msgstr "Status importu" + +#: views/lists/subscription/import-failed.hbs:4 +msgid "Failed addresses" +msgstr "Nieudane adresy" + +#: views/lists/subscription/import-failed.hbs:6 +msgid "see here" +msgstr "zobacz tutaj" + +#: views/lists/subscription/import-failed.hbs:10 +msgid "Fail reason" +msgstr "Przyczyna niepowodzenia" + +#: 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 "Zaimportuj subskrybentów" + +#: 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:55 +msgid "Example" +msgstr "Przykład" + +#: views/lists/subscription/import-preview.hbs:11 +msgid "Start import" +msgstr "Rozpocznij importowanie" + +#: views/lists/subscription/import.hbs:5 +msgid "CSV File" +msgstr "Plik CSV" + +#: views/lists/subscription/import.hbs:6 +msgid "CSV delimiter" +msgstr "Separator CSV" + +#: views/lists/subscription/import.hbs:7 +msgid "Categorize the imported subscribers as" +msgstr "Skategoryzuj importowanych subskrybentów jako" + +#: views/lists/subscription/import.hbs:8 routes/lists.js:171 +msgid "Subscribed" +msgstr "Subskrybuje" + +#: views/lists/subscription/import.hbs:9 +msgid "Regular subscriber addresses" +msgstr "Regularne adresy subskrybentów" + +#: views/lists/subscription/import.hbs:11 +msgid "Suppressed emails that will be unsubscribed from your list" +msgstr "Pomijane e-maile, które zostaną wypisane z listy" + +#: views/lists/view.hbs:3 +msgid "Subscription Form" +msgstr "Formularz subskrypcji" + +#: views/lists/view.hbs:4 +msgid "List Actions" +msgstr "Lista akcji" + +#: views/lists/view.hbs:8 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 "Utwórz wyzwalacz" + +#: views/lists/view.hbs:9 +msgid "Add Subscriber" +msgstr "Dodaj subskrybenta" + +#: views/lists/view.hbs:10 +msgid "Import Subscribers" +msgstr "Importuj subskrybentów" + +#: views/lists/view.hbs:14 +msgid "Create New Segment" +msgstr "Utwórz nowy segment" + +#: views/lists/view.hbs:15 +msgid "Filter" +msgstr "Filtr" + +#: views/lists/view.hbs:16 +msgid "Subscriptions" +msgstr "Subskrypcje" + +#: views/lists/view.hbs:17 +msgid "Imports" +msgstr "Import" + +#: views/lists/view.hbs:24 routes/campaigns.js:266 routes/lists.js:265 +msgid "Finished" +msgstr "Skończone" + +#: views/lists/view.hbs:26 +msgid "Added" +msgstr "Dodany" + +#: views/lists/view.hbs:27 +msgid "Updated" +msgstr "Zaktualizowano" + +#: views/lists/view.hbs:28 +msgid "Failed" +msgstr "Nie powiodło się" + +#: views/lists/view.hbs:30 +msgid "" +"Are you sure? This action should only be called to resolve stalled imports" +msgstr "" +"Jesteś pewny? Ta czynność powinna byś wywołana tylko w celu rozwiązania " +"impasu importu." + +#: views/lists/view.hbs:31 +msgid "Restart" +msgstr "Uruchom ponownie" + +#: views/partials/codeeditor.hbs:1 views/partials/summernote.hbs:1 +msgid "Template content (HTML)" +msgstr "Zawartość szablonu (HTML)" + +#: views/partials/html-preview.hbs:1 +msgid "Toggle HTML preview" +msgstr "Przełącz podgląd HTML" + +#: views/partials/merge-tag-reference.hbs:1 +msgid "Merge tag reference" +msgstr "Odniesienie merge tag" + +#: 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 tagi są tagami, które są zastępowane przed wysłaniem wiadomości. " +"Format merge tagu jest następujący: [TAG_NAME] lub " +"[TAG_NAME/fallback] gdzie fallback jest opcjonalną " +"wartością tekstową używaną, gdy TAG_NAME jest pusty." + +#: views/partials/plaintext.hbs:1 +msgid "Template content (plaintext)" +msgstr "Treść szablonu (zwykły tekst)" + +#: views/settings.hbs:5 +msgid "Service Address (URL)" +msgstr "Adres usługi (URL)" + +#: views/settings.hbs:6 +msgid "Enter the URL this service can be reached from" +msgstr "Wprowadź adres URL, z którego można uzyskać tę usługę" + +#: views/settings.hbs:7 +msgid "Admin Email" +msgstr "E-mail Administratora" + +#: views/settings.hbs:8 +msgid "" +"Enter the email address that will be used as \"from\" for system messages" +msgstr "" +"Wprowadź adres e-mail, który będzie używany jako \"from \" dla wiadomości " +"systemowych" + +#: views/settings.hbs:9 +msgid "Disable WYSIWYG editor" +msgstr "Wyłącz edytor WYSIWYG" + +#: views/settings.hbs:10 +msgid "If checked then message editor displays HTML code without the preview" +msgstr "Jeśli zaznaczone, edytor wiadomości wyświetla kod HTML bez podglądu" + +#: views/settings.hbs:11 +msgid "Disable subscription confirmation messages" +msgstr "Wyłącz wiadomości potwierdzające subskrypcję" + +#: 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 "" +"Jeśli jest zaznaczone, nie wysyłaj wiadomości potwierdzającej, że " +"subskrybent jest teraz subskrybowany lub nie subskrybowany. Nie wyłącza to " +"podwójnych wiadomości opt-in." + +#: views/settings.hbs:13 +msgid "Tracking ID" +msgstr "Identyfikator śledzenia" + +#: views/settings.hbs:14 +msgid "Enter Google Analytics tracking code" +msgstr "Wpisz kod śledzenia Google Analytics" + +#: views/settings.hbs:15 +msgid "Frontpage shout out" +msgstr "" + +#: views/settings.hbs:16 +msgid "HTML code shown in the front page header section" +msgstr "Kod HTML wyświetlany w sekcji nagłówka na pierwszej stronie" + +#: views/settings.hbs:17 +msgid "Campaign defaults" +msgstr "Domyślne ustawienia kampanii" + +#: views/settings.hbs:18 +msgid "Sender name" +msgstr "Nazwa nadawcy" + +#: views/settings.hbs:19 +msgid "Sender name, eg. My Awesome Company Ltd." +msgstr "Nazwa nadawcy, np. My Awesome Company Ltd." + +#: views/settings.hbs:20 +msgid "Default address" +msgstr "Domyślny adres" + +#: views/settings.hbs:21 +msgid "" +"Contact address to provide, eg. 1234 Main Street, Anywhere, MA 01234, USA" +msgstr "" +"Adres kontaktowy do dostarczenia, np. 1234 Main Street, Anywhere, MA 01234, " +"USA" + +#: views/settings.hbs:22 +msgid "Default \"from name\"" +msgstr "Domyślna nazwa \"from\"" + +#: views/settings.hbs:24 +msgid "Default \"from\" email" +msgstr "Domyślny e-mail \"from\"" + +#: views/settings.hbs:26 +msgid "Default \"subject line\"" +msgstr "Domyślny temat \"from\"" + +#: views/settings.hbs:28 +msgid "Default homepage (URL)" +msgstr "Domyślna strona główna (URL)" + +#: views/settings.hbs:29 +msgid "URL to redirect the subscribed users to, eg. http://example.com/" +msgstr "Adres URL do przekierowania subskrybentów do, np. http://example.com/" + +#: views/settings.hbs:30 +msgid "Mailer Settings" +msgstr "Ustawienia Mailera" + +#: views/settings.hbs:31 +msgid "These settings are required to send out e-mail messages" +msgstr "Te ustawienia są wymagane do wysyłania wiadomości e-mail" + +#: 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 "Użyj SMTP do wysyłania poczty" + +#: views/settings.hbs:35 +msgid "Hostname" +msgstr "Nazwa hosta" + +#: views/settings.hbs:36 +msgid "Port" +msgstr "Port" + +#: views/settings.hbs:37 +msgid "Port, eg. 465. Autodetected if left blank" +msgstr "Port, np. 465. Autodetekcja, jeśli pozostanie pusta" + +#: views/settings.hbs:38 +msgid "Encryption" +msgstr "Szyfrowanie" + +#: views/settings.hbs:39 +msgid "Disable SMTP authentication" +msgstr "Wyłącz uwierzytelnianie SMTP" + +#: views/settings.hbs:40 views/users/forgot.hbs:9 views/users/login.hbs:4 +#: views/users/login.hbs:5 +msgid "Username" +msgstr "Nazwa użytkownika" + +#: views/settings.hbs:41 +msgid "Username, eg. myaccount@example.com" +msgstr "Nazwa użytkownika, np. myaccount@example.com" + +#: views/settings.hbs:42 views/settings.hbs:43 views/users/login.hbs:6 +#: views/users/login.hbs:7 +msgid "Password" +msgstr "Hasło" + +#: views/settings.hbs:44 +msgid "Use SES API for sending mail" +msgstr "Użyj interfejsu API SES do wysyłania poczty" + +#: views/settings.hbs:45 +msgid "Access Key" +msgstr "Access Key" + +#: views/settings.hbs:46 +msgid "AWS Access Key Id" +msgstr "AWS Access Key Id" + +#: views/settings.hbs:47 +msgid "Secret Key" +msgstr "Secret Key" + +#: views/settings.hbs:48 +msgid "AWS Secret Access Key" +msgstr "AWS Secret Access Key" + +#: views/settings.hbs:49 +msgid "Region" +msgstr "Region" + +#: views/settings.hbs:50 +msgid "Checking" +msgstr "Sprawdzanie" + +#: views/settings.hbs:51 +msgid "Check Mailer config" +msgstr "Sprawdź konfigurację Mailera" + +#: views/settings.hbs:52 +msgid "Don't have an SMTP account yet? Create a free SendPulse account" +msgstr "Nie masz jeszcze konta SMTP? Utwórz darmowe konto SendPulse" + +#: views/settings.hbs:53 +msgid "here" +msgstr "tutaj" + +#: views/settings.hbs:54 +msgid "Advanced Mailer settings" +msgstr "Zaawansowane ustawienia Mailera" + +#: views/settings.hbs:55 +msgid "Log SMTP transactions" +msgstr "Loguj transakcje SMTP" + +#: views/settings.hbs:56 +msgid "Allow self-signed certificates" +msgstr "Zezwalaj na certyfikaty z podpisem własnym" + +#: views/settings.hbs:57 +msgid "Max connections" +msgstr "Maks. połączeń" + +#: views/settings.hbs:58 +msgid "The count of max connections, eg. 10" +msgstr "Liczba maksymalnych połączeń, np. 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 "" +"Liczba maksymalnych równoczesnych połączeń z serwerem SMTP (domyślnie 5). " +"Ten limit dotyczy procesu wysyłania." + +#: views/settings.hbs:60 +msgid "Max messages" +msgstr "Maks. wiadomości" + +#: views/settings.hbs:61 +msgid "The count of max messages, eg. 100" +msgstr "Liczba maksymalnych wiadomości, np. 100" + +#: views/settings.hbs:62 +msgid "" +"he number of messages to send through a single connection before the " +"connection is closed and reopened (defaults to 100)" +msgstr "" +"Liczba wiadomości do wysłania za pośrednictwem jednego połączenia, zanim " +"połączenie zostanie zamknięty i ponownie otwarty (domyślnie 100)" + +#: views/settings.hbs:63 +msgid "Throttling" +msgstr "Dławienie (Throttling)" + +#: views/settings.hbs:64 +msgid "Messages per hour eg. 1000" +msgstr "Wiadomości na godzinę, np. 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 "" +"Maksymalna liczba wiadomości do wysłania w ciągu godziny. Pozostaw puste lub " +"zero dla barku dławienia. Jeśli Twój dostawca wykorzystuje inny limit " +"prędkości (wiadomości/minutę lub wiadomości/sekundę), zamień ten limit na " +"wiadomości/godzinę (1m/s => 3600m/h). Ten limit dotyczy procesu wysyłania." + +#: views/settings.hbs:66 +msgid "VERP bounce handling" +msgstr "Obsługa odbijania 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 może wykorzystywać routing oparty na VERP do wykrywania odbić. W " +"takim przypadku wiadomość zostanie wysłana do adresata przy użyciu " +"niestandardowego adresu VERP jako ścieżki powrotnej wiadomości. Jeśli " +"wiadomość nie zostanie przyjęta, wiadomość odrzucona wiadomość zostanie " +"wysłana na specjalny adres VERP, a tym samym wykryte zostanie odrzucenie." + +#: 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 "" +"Aby uruchomić działanie VERP, musisz skonfigurować rekord MX DNS, który " +"wskazuje twoją nazwę hosta Mailtrain. Musisz także upewnić się, że interfejs " +"Mailtrain VERP jest dostępny z portu 25 serwera (port 25 zwykle wymaga " +"uprawnień użytkownika root). W ten sposób, jeśli ktoś spróbuje wysłać " +"wiadomość e-mail do someuser@verp-hostname, wówczas wiadomość e-mail powinna " +"dotrzeć do tego serwera." + +#: 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 "" +"VERP zwykle działa tylko wtedy, gdy używasz własnego serwera SMTP. Regularne " +"usługi przekazywania (SES, SparkPost, Gmail itp.) Mają tendencję do usuwania " +"adresu VERP z wiadomości." + +#: views/settings.hbs:70 +msgid "Use VERP to catch bounces" +msgstr "Użyj VERP do wykrycia odbicia" + +#: views/settings.hbs:71 +msgid "Server hostname" +msgstr "Nazwa hosta serwera" + +#: views/settings.hbs:72 +msgid "The VERP server hostname, eg. bounces.example.com" +msgstr "Nazwa hosta serwera VERP, np. bounces.example.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 "" +"Nazwa hosta serwera obsługi odrzutów VERP. Ta nazwa hosta jest używana w " +"SMTP jako \"od\", a rekordy MX DNS powinny wskazywać na ten serwer" + +#: views/settings.hbs:74 +msgid "" +"VERP bounce handling server is not enabled. Modify your server configuration " +"file and restart server to enable it" +msgstr "" +"Serwer obsługi odrzutów VERP nie jest włączony. Zmodyfikuj plik " +"konfiguracyjny serwera i zrestartuj serwer, aby go włączyć." + +#: views/settings.hbs:75 +msgid "GPG Signing" +msgstr "Podpis 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 "" +"Tylko wiadomości, które są zaszyfrowane, mogą być podpisane. Subskrybenci, " +"którzy nie ustawili klucza publicznego GPG w swoim profilu, otrzymują zwykłe " +"wiadomości e-mail. Użytkownicy z zestawem kluczy GPG otrzymują zaszyfrowane " +"wiadomości i jeśli masz ustawiony również klucz podpisu, wiadomości są " +"podpisywane tym kluczem." + +#: views/settings.hbs:77 +msgid "" +"Do not use sensitive keys here. The private key and passphrase are not " +"encrypted in the database." +msgstr "" +"Nie używaj tutaj wrażliwych kluczy. Klucz prywatny i hasło nie są szyfrowane " +"w bazie danych." + +#: views/settings.hbs:78 +msgid "Private Key Passphrase" +msgstr "Hasło klucza prywatnego" + +#: views/settings.hbs:79 +msgid "Passphrase for the key if set" +msgstr "Hasło dla klucza, jeśli jest ustawione" + +#: views/settings.hbs:80 +msgid "Only fill this if your private key is encrypted with a passphrase" +msgstr "Wypełnij to tylko, jeśli Twój klucz prywatny jest zaszyfrowany hasłem" + +#: views/settings.hbs:81 +msgid "GPG Private Key" +msgstr "Klucz prywatny 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 "" +"Ta wartość jest opcjonalna. Jeśli nie podasz klucza prywatnego GPG, " +"zaszyfrowane wiadomości są wysyłane bez podpisu." + +#: views/settings.hbs:84 +msgid "DKIM Signing by ZoneMTA" +msgstr "Podpisywanie DKIM przez 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 "" +"Jeśli używasz ZoneMTA, Mailtrain może dostarczyć klucz DKIM do podpisania " +"wszystkich wychodzących wiadomości. Inne usługi zwykle zapewniają własne " +"środki do podpisywania wiadomości przez DKIM" + +#: views/settings.hbs:86 +msgid "" +"Do not use sensitive keys here. The private key is not encrypted in the " +"database." +msgstr "" +"Nie używaj tutaj wrażliwych kluczy. Klucz prywatny nie jest zaszyfrowany w " +"bazie danych." + +#: views/settings.hbs:87 +msgid "ZoneMTA DKIM API Key" +msgstr "Klucz API ZoneMTA DKIM" + +#: views/settings.hbs:88 +msgid "Some secret value" +msgstr "Tajna wartość" + +#: 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 "" +"Tajna wartość znana ZoneMTA dla żądań klucza DKIM. Jeśli ta wartość została " +"wygenerowana przez skrypt instalacyjny Mailtrain, możesz zachować ją w " +"niezmienionym stanie" + +#: views/settings.hbs:90 +msgid "DKIM domain" +msgstr "Domena DKIM" + +#: views/settings.hbs:91 +msgid "Domain name for the DKIM key" +msgstr "Nazwa domeny dla klucza DKIM" + +#: views/settings.hbs:92 +msgid "Leave blank to use the sender email address domain" +msgstr "Pozostaw puste, aby użyć domeny adresu e-mail nadawcy" + +#: views/settings.hbs:93 views/settings.hbs:94 +msgid "DKIM key selector" +msgstr "Selektor klucza DKIM" + +#: views/settings.hbs:95 +msgid "Signing is disabled without a valid selector value" +msgstr "Podpisywanie jest wyłączone bez prawidłowej wartości selektora" + +#: views/settings.hbs:96 +msgid "DKIM Private Key" +msgstr "Klucz prywatny DKIM" + +#: views/settings.hbs:98 +msgid "" +"This value is optional. If you do not provide a private key then messages " +"are not signed." +msgstr "" +"Ta wartość jest opcjonalna. Jeśli nie dostarczysz klucza prywatnego, " +"wiadomości nie będą podpisane." + +#: views/subscription/confirm-notice.hbs:1 views/subscription/subscribe.hbs:1 +msgid "Warning!" +msgstr "Ostrzeżenie!" + +#: views/subscription/confirm-notice.hbs:2 +msgid "If JavaScript was not enabled then no confirmation message was sent" +msgstr "Jeśli JavaScript nie był włączony, nie wysłano żadnego potwierdzenia" + +#: views/subscription/confirm-notice.hbs:3 +msgid "Almost finished." +msgstr "Prawie gotowe." + +#: views/subscription/confirm-notice.hbs:4 +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 "" +"Musimy potwierdzić swój adres e-mail. Aby zakończyć proces subskrypcji, " +"kliknij link w wiadomości e-mail, którą właśnie wysłaliśmy." + +#: views/subscription/confirm-notice.hbs:5 +#: views/subscription/unsubscribe-notice.hbs:3 +#: views/subscription/updated-notice.hbs:3 +msgid "return to our website" +msgstr "powrót do strony" + +#: views/subscription/manage-address.hbs:1 +msgid "Update your Email Address" +msgstr "Zaktualizuj swój adres e-mail" + +#: views/subscription/manage-address.hbs:2 +msgid "Existing Email Address" +msgstr "Istniejący adres e-mail" + +#: views/subscription/manage-address.hbs:3 +msgid "New Email Address" +msgstr "Nowy adres e-mail" + +#: views/subscription/manage-address.hbs:4 +msgid "Your new email address" +msgstr "Twój nowy adres e-mail" + +#: views/subscription/manage-address.hbs:5 +msgid "" +"You will receive a confirmation request to your new email address that you " +"need to accept before your email is actually changed" +msgstr "" +"Otrzymasz prośbę o potwierdzenie na nowy adres e-mail, który musisz " +"zaakceptować przed faktyczną zmianą adresu e-mail" + +#: views/subscription/manage-address.hbs:6 +msgid "Update Email Address" +msgstr "Zaktualizuj adres e-mail" + +#: views/subscription/manage.hbs:1 +msgid "Update your preferences" +msgstr "Zaktualizuj swoje preferencje" + +#: views/subscription/manage.hbs:3 +msgid "want to change it?" +msgstr "chcesz to zmienić?" + +#: views/subscription/manage.hbs:6 views/subscription/subscribe.hbs:6 +msgid "Download signature verification key" +msgstr "Pobierz klucz weryfikacji podpisu" + +#: views/subscription/manage.hbs:8 views/subscription/subscribe.hbs:8 +msgid "" +"Insert your GPG public key here to encrypt messages sent to your address" +msgstr "" +"Wstaw tutaj swój publiczny klucz GPG, aby zaszyfrować wiadomości wysłane na " +"Twój adres" + +#: views/subscription/manage.hbs:9 views/subscription/subscribe.hbs:9 +msgid "optional" +msgstr "opcjonalny" + +#: views/subscription/manage.hbs:11 +msgid "Update Profile" +msgstr "Zaktualizuj profil" + +#: views/subscription/subscribe.hbs:2 +msgid "JavaScript must be enabled in order for the subscription form to work" +msgstr "" +"Aby formularz subskrypcji działał, musi być włączona obsługa JavaScript" + +#: views/subscription/subscribe.hbs:11 +msgid "Subscribe to list" +msgstr "Zapisz się do listy" + +#: views/subscription/subscribed.hbs:3 +msgid "Thank you for subscribing!" +msgstr "Dziękujemy za subskrypcję!" + +#: views/subscription/subscribed.hbs:4 +msgid "continue to our website" +msgstr "przejdź na naszą stronę internetową" + +#: views/subscription/unsubscribe-notice.hbs:1 +msgid "Unsubscribe Successful" +msgstr "Anulowanie subskrypcji przebiegło pomyślnie" + +#: views/subscription/unsubscribe-notice.hbs:2 +msgid "You have been removed from:" +msgstr "Zostałeś usunięty z:" + +#: views/subscription/unsubscribe.hbs:2 +msgid "Enter your email address to unsubscribe from:" +msgstr "Wpisz swój adres e-mail, aby zrezygnować z subskrypcji:" + +#: views/subscription/updated-notice.hbs:1 +msgid "Profile Updated" +msgstr "Profil zaktualizowany" + +#: views/subscription/updated-notice.hbs:2 +msgid "Your profile information has been updated." +msgstr "Twoje informacje profilowe zostały zaktualizowane." + +#: views/templates/create.hbs:2 views/templates/edit.hbs:2 +#: views/templates/templates.hbs:2 views/templates/templates.hbs:4 +#: lib/tools.js:115 +msgid "Templates" +msgstr "Szablony" + +#: views/templates/create.hbs:3 views/templates/create.hbs:4 +#: views/templates/create.hbs:12 views/templates/templates.hbs:3 +msgid "Create Template" +msgstr "Utwórz szablon" + +#: views/templates/create.hbs:5 views/templates/edit.hbs:6 +msgid "Template name" +msgstr "Nazwa szablonu" + +#: views/templates/create.hbs:6 views/templates/edit.hbs:7 +msgid "Name for this template, eg. Newsletter" +msgstr "Nazwa tego szablonu, np. Newsletter" + +#: views/templates/create.hbs:7 +msgid "HTML Editor" +msgstr "Edytor HTML" + +#: views/templates/create.hbs:10 views/templates/edit.hbs:9 +msgid "Optional comments about this template" +msgstr "Opcjonalne komentarze na temat tego szablonu" + +#: views/templates/edit.hbs:3 views/templates/edit.hbs:4 +msgid "Edit Template" +msgstr "Edytuj szablon" + +#: views/templates/edit.hbs:5 +msgid "Back to templates" +msgstr "Powrót do szablonów" + +#: views/templates/edit.hbs:11 +msgid "Delete Template" +msgstr "Usuń Szablon" + +#: 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 "Automatyczne wyzwalacze" + +#: views/triggers/create-select.hbs:5 +msgid "Select a list for the trigger" +msgstr "Wybierz listę dla wyzwalacza" + +#: views/triggers/create.hbs:5 views/triggers/edit.hbs:6 +msgid "Trigger name" +msgstr "Nazwa wyzwalacza" + +#: views/triggers/create.hbs:6 views/triggers/edit.hbs:7 +msgid "Name for this trigger, eg. Inactive subscribers" +msgstr "Nazwa tego wyzwalacza, np. Nieaktywni subskrybenci" + +#: views/triggers/create.hbs:8 views/triggers/edit.hbs:9 +msgid "Optional comments about this trigger" +msgstr "Opcjonalne komentarze na temat tego wyzwalacza" + +#: views/triggers/create.hbs:12 views/triggers/edit.hbs:14 +msgid "Trigger rule" +msgstr "Reguła wyzwalacza" + +#: views/triggers/create.hbs:13 views/triggers/edit.hbs:15 +msgid "Trigger fires" +msgstr "Aktywacja wyzwalacza" + +#: views/triggers/create.hbs:14 views/triggers/edit.hbs:16 +msgid "days after:" +msgstr "dni po:" + +#: views/triggers/create.hbs:15 views/triggers/edit.hbs:17 +msgid "Subscription" +msgstr "Subskrypcja" + +#: views/triggers/create.hbs:16 views/triggers/create.hbs:21 +#: views/triggers/edit.hbs:18 views/triggers/edit.hbs:23 +msgid "Event" +msgstr "Zdarzenie" + +#: 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 "Kampania" + +#: views/triggers/create.hbs:23 views/triggers/edit.hbs:25 +msgid "Trigger action" +msgstr "Czynność wyzwalacza" + +#: views/triggers/create.hbs:24 views/triggers/edit.hbs:26 +msgid "Send campaign" +msgstr "Wyślij kampanię" + +#: views/triggers/edit.hbs:3 views/triggers/edit.hbs:4 +msgid "Edit Trigger" +msgstr "Edycja wyzwalacza" + +#: views/triggers/edit.hbs:5 +msgid "Back to triggers" +msgstr "Powrót do wyzwalaczy" + +#: views/triggers/edit.hbs:11 +msgid "Trigger is enabled" +msgstr "Wyzwalacz jest włączony" + +#: views/triggers/edit.hbs:29 +msgid "Delete Trigger" +msgstr "Usuń wyzwalacz" + +#: views/triggers/triggered.hbs:3 +msgid "Triggered" +msgstr "Wywołany" + +#: views/triggers/triggered.hbs:4 +msgid "Triggered subscribers" +msgstr "Wywołani subskrybenci" + +#: views/triggers/triggered.hbs:5 +msgid "Subscribers who caused this trigger to fire" +msgstr "Subskrybenci, którzy spowodowali uruchomienie tego wyzwalacza" + +#: views/triggers/triggered.hbs:9 +msgid "Triggered time" +msgstr "Czas wywołanych" + +#: views/triggers/triggers.hbs:9 +msgid "Trigger" +msgstr "Wyzwalacz" + +#: views/triggers/triggers.hbs:10 +msgid "Target Campaign" +msgstr "Cel kampanii" + +#: views/triggers/triggers.hbs:11 +msgid "Triggered count" +msgstr "Liczba wyzwoleń" + +#: views/triggers/triggers.hbs:12 +msgid "Enabled" +msgstr "Włączone" + +#: views/triggers/triggers.hbs:13 +msgid "Disabled" +msgstr "Wyłączone" + +#: views/users/account.hbs:4 +msgid "This account is managed through LDAP." +msgstr "To konto jest zarządzane przez LDAP." + +#: views/users/account.hbs:5 +msgid "Associated Email Address" +msgstr "Powiązany adres e-mail" + +#: views/users/account.hbs:8 +msgid "Your e-mail address" +msgstr "Twój adres e-mail" + +#: views/users/account.hbs:9 +msgid "" +"This address is used for account recovery in case you lose your password" +msgstr "Ten adres służy do odzyskiwania konta na wypadek utraty hasła" + +#: views/users/account.hbs:10 +msgid "Password change" +msgstr "Zmiana hasła" + +#: views/users/account.hbs:11 +msgid "" +"You only need to fill out this form if you want to change your current " +"password" +msgstr "Wypełnij ten formularz, jeśli chcesz zmienić swoje obecne hasło" + +#: views/users/account.hbs:12 views/users/account.hbs:13 +msgid "Current Password" +msgstr "Aktualne hasło" + +#: views/users/account.hbs:14 views/users/account.hbs:15 +#: views/users/reset.hbs:6 views/users/reset.hbs:7 +msgid "New Password" +msgstr "Nowe hasło" + +#: views/users/account.hbs:16 +msgid "Confirm Password" +msgstr "Powtórz hasło" + +#: views/users/account.hbs:17 views/users/reset.hbs:8 +msgid "Confirm New Password" +msgstr "Potwierdź nowe hasło" + +#: views/users/api.hbs:4 +msgid "Are you sure? Resetting would invalidate the currently existing token." +msgstr "Jesteś pewny? Zresetowanie unieważni istniejący token." + +#: views/users/api.hbs:5 +msgid "Are you sure?" +msgstr "Jesteś pewny?" + +#: views/users/api.hbs:6 +msgid "Reset Access Token" +msgstr "Zresetuj Access Token" + +#: views/users/api.hbs:7 +msgid "Generate Access Token" +msgstr "Wygeneruj Access Token" + +#: views/users/api.hbs:8 +msgid "Personal access token:" +msgstr "Osobisty Access Token:" + +#: views/users/api.hbs:9 +msgid "Access token not yet generated" +msgstr "Access token nie został jeszcze wygenerowany" + +#: views/users/api.hbs:10 +msgid "Notes about the API" +msgstr "Uwagi na temat interfejsu 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 "" +"Odpowiedź API to struktura JSON z właściwościami error i " +"data. Jeśli odpowiedź error ma ustawioną " +"wartość, to żądanie nie powiodło się." + +#: 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 "" +"Musisz zdefiniować właściwy Content-Type podczas wysyłania " +"zapytania. Możesz użyć application/x-www-form-urlencoded dla " +"normalnych danych formularza lub application/json dla JSON " +"payload. Używanie multipart/form-data nie jest obsługiwane." + +#: views/users/api.hbs:13 +msgid "Add subscription" +msgstr "Dodaj subskrypcję" + +#: 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 "" +"To wywołanie API wprowadza nową subskrypcję lub aktualizacje istniejące. " +"Pola nieuwzględnione pozostają bez zmian, więc jeśli zaktualizujesz tylko " +"wartość LAST_NAME, to FIRST_NAME pozostanie nietknięte dla istniejącej " +"subskrypcji." + +#: 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:48 +msgid "arguments" +msgstr "argumenty" + +#: views/users/api.hbs:16 views/users/api.hbs:31 views/users/api.hbs:39 +#: views/users/api.hbs:47 +msgid "your personal access token" +msgstr "twój osobisty token dostępu" + +#: views/users/api.hbs:18 views/users/api.hbs:33 views/users/api.hbs:41 +msgid "subscriber's email address" +msgstr "subscriber's email addressadres e-mail subskrybenta" + +#: views/users/api.hbs:19 views/users/api.hbs:34 views/users/api.hbs:42 +#: views/users/api.hbs:50 +msgid "required" +msgstr "wymagany" + +#: views/users/api.hbs:20 +msgid "subscriber's first name" +msgstr "imię subskrybenta" + +#: views/users/api.hbs:21 +msgid "subscriber's last name" +msgstr "nazwisko subskrybenta" + +#: views/users/api.hbs:22 +msgid "" +"subscriber's timezone (eg. \"Europe/Tallinn\", \"PST\" or \"UTC\"). If not " +"set defaults to \"UTC\"" +msgstr "" +"strefa czasowa subskrybenta (np. \"Europa/Tallinn\", \"PST\" lub \"UTC\"). " +"Jeśli nie jest ustawiony domyślnie „UTC”" + +#: views/users/api.hbs:23 +msgid "" +"custom field value. Use yes/no for option group values (checkboxes, radios, " +"drop downs)" +msgstr "" +"niestandardowa wartość pola. Użyj wartości tak/nie dla wartości grup opcji " +"(checkbox, radio, lista rozwijana)" + +#: views/users/api.hbs:24 +msgid "Additional POST arguments" +msgstr "Dodatkowe argumenty 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 "" +"ustawić na \"tak \", jeśli chcesz się upewnić, że wiadomość e-mail została " +"oznaczona jako zasubskrybowana, nawet jeśli została wcześniej oznaczona jako " +"niesubskrybowana. Jeśli e-mail został już wypisany/zablokowane, status " +"subskrypcji nie zostanie zmieniony" + +#: 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 "" +"ustaw na \"tak\", jeśli chcesz wysłać wiadomość e-mail z potwierdzeniem do " +"subskrybenta, zanim faktycznie zostaniesz oznaczony jako zasubskrybowany" + +#: views/users/api.hbs:28 +msgid "Remove subscription" +msgstr "Usuń subskrypcję" + +#: views/users/api.hbs:29 +msgid "This API call marks a subscription as unsubscribed" +msgstr "To wywołanie API oznacza subskrypcję jako niesubskrybowaną" + +#: views/users/api.hbs:36 +msgid "Delete subscription" +msgstr "Usuń subskrypcję" + +#: views/users/api.hbs:37 +msgid "This API call deletes a subscription" +msgstr "To wywołanie API usuwa subskrypcję" + +#: views/users/api.hbs:44 +msgid "Add new custom field" +msgstr "Dodaj nowe niestandardowe pole" + +#: views/users/api.hbs:45 +msgid "This API call creates a new custom field for a list." +msgstr "To wywołanie API tworzy nowe niestandardowe pole dla listy." + +#: views/users/api.hbs:49 +msgid "field name" +msgstr "nazwa pola" + +#: views/users/api.hbs:51 +msgid "one of the following types:" +msgstr "jeden z następujących typów:" + +#: views/users/api.hbs:52 +msgid "" +"If the type is 'option' then you also need to specify the parent element ID" +msgstr "Jeśli typ to \"option\", musisz również podać ID elementu nadrzędnego" + +#: views/users/api.hbs:53 +msgid "" +"Template for the group element. If not set, then values of the elements are " +"joined with commas" +msgstr "" +"Szablon dla elementu grupy. Jeśli nie jest ustawione, wartości elementów są " +"połączone przecinkami" + +#: views/users/api.hbs:54 +msgid "" +"if not visible then the subscriber can not view or modify this value at the " +"profile page" +msgstr "" +"jeśli niewidoczny, subskrybent nie może wyświetlić ani zmodyfikować tej " +"wartości na stronie profilu" + +#: views/users/forgot.hbs:3 views/users/reset.hbs:3 +msgid "Password Reset" +msgstr "Resetowanie hasła" + +#: views/users/forgot.hbs:4 +msgid "Reset your password?" +msgstr "Nie pamiętasz hasła?" + +#: views/users/forgot.hbs:5 +msgid "Accounts are managed through LDAP." +msgstr "Konta są zarządzane przez LDAP." + +#: views/users/forgot.hbs:6 views/users/reset.hbs:10 +msgid "Reset Password" +msgstr "Resetuj hasło" + +#: 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 "" +"Podaj nazwę użytkownika lub adres e-mail użyty podczas rejestracji konta " +"Mailtrain." + +#: views/users/forgot.hbs:8 +msgid "We will send you an email that will allow you to reset your password." +msgstr "Wyślemy Ci wiadomość e-mail, która pozwoli Ci zresetować hasło." + +#: views/users/forgot.hbs:10 +msgid "Username or email address" +msgstr "Nazwa użytkownika lub adres e-mail" + +#: views/users/forgot.hbs:11 +msgid "Send verification email" +msgstr "Wyślij email weryfikacyjny" + +#: views/users/login.hbs:8 +msgid "Remember me" +msgstr "Zapamiętaj mnie" + +#: views/users/login.hbs:11 views/users/login.hbs:12 +msgid "Forgot password?" +msgstr "Zapomniałeś hasła?" + +#: views/users/reset.hbs:4 +msgid "Choose your new password" +msgstr "Wybierz nowe hasło" + +#: views/users/reset.hbs:5 +msgid "Please enter a new password." +msgstr "Wprowadź nowe hasło." + +#: lib/feed.js:31 +msgid "Bad status code %s" +msgstr "Nieprawidłowy kod statusu %s" + +#: lib/helpers.js:17 +msgid "URL that points to the unsubscribe page" +msgstr "Adres URL wskazujący stronę rezygnacji z subskrypcji" + +#: lib/helpers.js:20 +msgid "URL that points to the preferences page of the subscriber" +msgstr "Adres URL wskazujący stronę preferencji subskrybenta" + +#: lib/helpers.js:23 +msgid "URL to preview the message in a browser" +msgstr "URL, aby wyświetlić podgląd wiadomości w przeglądarce" + +#: lib/helpers.js:29 lib/models/segments.js:31 +msgid "First name" +msgstr "Imię" + +#: lib/helpers.js:32 lib/models/segments.js:35 +msgid "Last name" +msgstr "Nazwisko" + +#: lib/helpers.js:35 +msgid "Full name (first and last name combined)" +msgstr "Imię i nazwisko" + +#: lib/helpers.js:38 +msgid "Unique ID that identifies the recipient" +msgstr "Unikalny identyfikator identyfikujący odbiorcę" + +#: lib/helpers.js:41 +msgid "Unique ID that identifies the list used for this campaign" +msgstr "Unikalny identyfikator identyfikujący listę użytą w tej kampanii" + +#: lib/helpers.js:44 +msgid "Unique ID that identifies current campaign" +msgstr "Unikalny identyfikator identyfikujący bieżącą kampanię" + +#: lib/mailer.js:215 +msgid "Invalid mail transport" +msgstr "Nieprawidłowy transport poczty" + +#: lib/models/campaigns.js:271 lib/models/campaigns.js:298 +#: lib/models/campaigns.js:371 lib/models/campaigns.js:494 +#: lib/models/campaigns.js:752 lib/models/campaigns.js:881 +msgid "Missing Campaign ID" +msgstr "Brakujący identyfikator kampanii" + +#: lib/models/campaigns.js:407 +msgid "Emtpy or too large attahcment" +msgstr "Pusty lub zbyt duży załącznik" + +#: lib/models/campaigns.js:573 lib/models/campaigns.js:761 +msgid "Campaign Name must be set" +msgstr "Nazwa kampanii musi być ustawiona" + +#: lib/models/campaigns.js:577 +msgid "RSS URL must be set and needs to be a valid URL" +msgstr "" +"Adres URL kanału RSS musi być ustawiony i musi być prawidłowym adresem URL" + +#: lib/models/campaigns.js:730 +msgid "Selected template not found" +msgstr "Nie znaleziono wybranego szablonu" + +#: lib/models/campaigns.js:1082 +msgid "Invalid or missing message ID" +msgstr "Nieprawidłowy lub brakujący ID wiadomości" + +#: lib/models/fields.js:24 +msgid "Drop Down" +msgstr "Lista rozwijana" + +#: lib/models/fields.js:25 +msgid "Date (MM/DD/YYY)" +msgstr "Data (MM/DD/YYYY)" + +#: lib/models/fields.js:29 +msgid "JSON value for custom rendering" +msgstr "Wartość JSON dla renderowania niestandardowego" + +#: lib/models/fields.js:30 +msgid "Option" +msgstr "Opcja" + +#: lib/models/fields.js:53 lib/models/fields.js:98 lib/models/fields.js:123 +#: lib/models/lists.js:81 lib/models/lists.js:175 lib/models/lists.js:212 +#: lib/models/segments.js:43 lib/models/segments.js:176 +#: lib/models/subscriptions.js:88 lib/models/subscriptions.js:640 +#: lib/models/subscriptions.js:703 lib/models/subscriptions.js:889 +#: lib/models/subscriptions.js:992 lib/models/subscriptions.js:1046 +#: lib/models/subscriptions.js:1109 lib/models/subscriptions.js:1152 +msgid "Missing List ID" +msgstr "Brakujący ID listy" + +#: lib/models/fields.js:129 +msgid "Option field requires a group to be selected" +msgstr "Pole opcji wymaga wyboru grupy" + +#: lib/models/fields.js:149 lib/models/fields.js:199 +msgid "Missing Field ID" +msgstr "Brakujący ID pola" + +#: lib/models/fields.js:153 lib/models/segments.js:185 +#: lib/models/segments.js:225 +msgid "Field Name must be set" +msgstr "Nazwa pola musi być ustawiona" + +#: lib/models/fields.js:216 +msgid "Custom field not found" +msgstr "Nie znaleziono pola niestandardowego" + +#: lib/models/fields.js:289 +msgid "Unknown column type %s" +msgstr "Nieznany typ kolumny %s" + +#: lib/models/fields.js:293 +msgid "Missing column name" +msgstr "Brakująca nazwa kolumny" + +#: lib/models/fields.js:297 +msgid "Missing list ID" +msgstr "Brakujący ID listy" + +#: lib/models/fields.js:305 +msgid "Provided List ID not found" +msgstr "Nie znaleziono ID listy dostarczonych" + +#: lib/models/links.js:328 routes/campaigns.js:541 routes/campaigns.js:590 +#: services/sender.js:304 +msgid "Campaign not found" +msgstr "Nie znaleziono kampanii" + +#: lib/models/links.js:336 routes/lists.js:146 services/sender.js:311 +msgid "List not found" +msgstr "Nie znaleziono listy" + +#: lib/models/links.js:344 +msgid "Subscription not found" +msgstr "Nie znaleziono subskrypcji" + +#: lib/models/lists.js:117 lib/models/lists.js:179 +msgid "List Name must be set" +msgstr "Nazwa listy musi być ustawiona" + +#: lib/models/lists.js:241 +msgid "Missing List CID" +msgstr "Brakujący CID listy" + +#: lib/models/segments.js:15 +msgid "Signup country" +msgstr "Kraj rejestracji" + +#: lib/models/segments.js:19 lib/models/triggers.js:11 +msgid "Sign up date" +msgstr "Data rejestracji" + +#: lib/models/segments.js:23 lib/models/triggers.js:15 +msgid "Latest open" +msgstr "Ostatnie otwarcie" + +#: lib/models/segments.js:27 lib/models/triggers.js:19 +msgid "Latest click" +msgstr "Ostatnie kliknięcie" + +#: 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 "Brakujący ID segmentu" + +#: lib/models/segments.js:85 lib/models/segments.js:549 +#: lib/models/segments.js:658 +msgid "Segment not found" +msgstr "Nie znaleziono segmentu" + +#: 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 dni po dzisiejszym dniu" + +#: 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 dni przed dzisiejszym dniem" + +#: lib/models/segments.js:148 lib/models/segments.js:410 +msgid "today" +msgstr "dziś" + +#: lib/models/segments.js:189 lib/models/segments.js:229 +msgid "Invalid segment rule type" +msgstr "Nieprawidłowy typ reguły segmentu" + +#: 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 "Nie znaleziono wybranego segmentu" + +#: 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 "Nieprawidłowy typ reguły" + +#: lib/models/segments.js:358 lib/models/segments.js:434 +#: lib/models/segments.js:524 +msgid "Missing Rule ID" +msgstr "Brakujący ID reguły" + +#: lib/models/segments.js:374 +msgid "Specified rule not found" +msgstr "Nie znaleziono określonej reguły" + +#: lib/models/segments.js:385 +msgid "Specified segment not found" +msgstr "Nie znaleziono określonego segmentu" + +#: lib/models/segments.js:445 +msgid "Selected rule not found" +msgstr "Nie znaleziono wybranej reguły" + +#: lib/models/subscriptions.js:233 +msgid "%s: Please Confirm Subscription" +msgstr "%s: Potwierdź subskrypcję" + +#: lib/models/subscriptions.js:324 +msgid "Could not save subscription" +msgstr "Nie można zapisać subskrypcji" + +#: lib/models/subscriptions.js:507 lib/models/subscriptions.js:537 +msgid "Missing Subscription ID" +msgstr "Brakujący ID subskrypcji" + +#: lib/models/subscriptions.js:565 +msgid "Missing Subscription email address" +msgstr "Brak adresu e-mail subskrypcji" + +#: lib/models/subscriptions.js:644 lib/models/subscriptions.js:893 +#: lib/models/subscriptions.js:1156 +msgid "Missing subscription ID" +msgstr "Brakujący ID subskrypcji" + +#: lib/models/subscriptions.js:707 +msgid "Missing email address" +msgstr "Brakujący adres e-mail" + +#: lib/models/subscriptions.js:996 lib/models/subscriptions.js:1050 +#: lib/models/subscriptions.js:1086 +msgid "Missing Import ID" +msgstr "Brakujący ID importu" + +#: lib/models/subscriptions.js:1178 +msgid "Unknown subscription ID" +msgstr "Nieznany ID subskrypcji" + +#: lib/models/subscriptions.js:1183 +msgid "Nothing seems to be changed" +msgstr "Wydaje się, że nic nie zostało zmienione" + +#: lib/models/subscriptions.js:1197 +msgid "This address is already registered by someone else" +msgstr "Ten adres jest już zarejestrowany przez inną osobę" + +#: lib/models/templates.js:51 lib/models/templates.js:122 +#: lib/models/templates.js:163 +msgid "Missing Template ID" +msgstr "Brakujący ID szablonu" + +#: lib/models/templates.js:80 lib/models/templates.js:126 +msgid "Template Name must be set" +msgstr "Nazwa szablonu musi być ustawiona" + +#: lib/models/triggers.js:28 +msgid "Has Opened" +msgstr "Otwarte" + +#: lib/models/triggers.js:31 +msgid "Has Clicked" +msgstr "Kliknięte" + +#: lib/models/triggers.js:34 +msgid "Not Opened" +msgstr "Nie otwarte" + +#: lib/models/triggers.js:37 +msgid "Not Clicked" +msgstr "Nie kliknięte" + +#: lib/models/triggers.js:174 lib/models/triggers.js:211 +msgid "Missing or invalid list ID" +msgstr "Brakujący lub nieprawidłowy ID listy" + +#: lib/models/triggers.js:178 lib/models/triggers.js:263 +msgid "Days in the past are not allowed" +msgstr "Dni w przeszłości są niedozwolone" + +#: lib/models/triggers.js:182 lib/models/triggers.js:203 +#: lib/models/triggers.js:267 lib/models/triggers.js:288 +msgid "Missing or invalid trigger rule" +msgstr "Brakująca lub nieprawidłowa reguła wyzwalacza" + +#: lib/models/triggers.js:189 lib/models/triggers.js:274 +msgid "Invalid subscription configuration" +msgstr "ieprawidłowa konfiguracja subskrypcji" + +#: lib/models/triggers.js:196 lib/models/triggers.js:281 +msgid "Invalid campaign configuration" +msgstr "Nieprawidłowa konfiguracja kampanii" + +#: lib/models/triggers.js:199 lib/models/triggers.js:284 +msgid "A campaing can not be a target for itself" +msgstr "Kampania nie może być celem samym w sobie" + +#: lib/models/triggers.js:232 +msgid "Could not store trigger row" +msgstr "Nie można zapisać wiersza wyzwalacza" + +#: lib/models/triggers.js:249 +msgid "Missing or invalid Trigger ID" +msgstr "Brakujący lub nieprawidłowy ID wyzwalacza" + +#: lib/models/triggers.js:316 +msgid "Missing Trigger ID" +msgstr "Brakujący ID wyzwalacza" + +#: lib/models/users.js:103 +msgid "Could not store user row" +msgstr "Nie można zapisać wiersza użytkownika" + +#: lib/models/users.js:173 +msgid "Email Address must be set" +msgstr "Adres e-mail musi być ustawiony" + +#: lib/models/users.js:184 +msgid "Failed to check user data" +msgstr "Nie można sprawdzić danych użytkownika" + +#: lib/models/users.js:195 +msgid "" +"Can't change email as another user with the same email address already exists" +msgstr "" +"Nie można zmienić adresu e-mail, ponieważ inny użytkownik o tym samym " +"adresie e-mail już istnieje" + +#: lib/models/users.js:212 +msgid "Incorrect current password" +msgstr "Nieprawidłowe aktualne hasło" + +#: lib/models/users.js:216 +msgid "New password not set" +msgstr "Nowe hasło nie zostało ustawione" + +#: lib/models/users.js:220 +msgid "Passwords do not match" +msgstr "Hasła nie pasują do siebie" + +#: lib/models/users.js:258 +msgid "User ID not set" +msgstr "Nie ustawiono ID użytkownika" + +#: lib/models/users.js:286 +msgid "Username must be set" +msgstr "Nazwa użytkownika musi być ustawiona" + +#: lib/models/users.js:323 +msgid "Mailer password change request" +msgstr "Żądanie zmiany hasła Mailera" + +#: lib/models/users.js:347 lib/models/users.js:367 +msgid "Missing username or reset token" +msgstr "Brakująca nazwa użytkownika lub token reset" + +#: lib/models/users.js:371 +msgid "Invalid new password" +msgstr "Nieprawidłowe nowe hasło" + +#: lib/passport.js:38 +msgid "%s logged out" +msgstr "%s Wylogowany" + +#: lib/passport.js:51 +msgid "Failed to authenticate user" +msgstr "Nie udało się uwierzytelnić użytkownika" + +#: lib/passport.js:67 +msgid "Logged in as %s" +msgstr "Zalogowano jako %s" + +#: lib/passport.js:125 +msgid "Incorrect username or password" +msgstr "Nieprawidłowa nazwa użytkownika lub hasło" + +#: lib/tools.js:123 +msgid "Automation" +msgstr "Automatyzacja" + +#: lib/tools.js:133 +msgid "Blocked email address \"%s\"" +msgstr "Zablokowany adres e-mail \"%s\"" + +#: lib/tools.js:142 +msgid "Invalid email address \"%s\"." +msgstr "Nieprawidłowy adres e-mail \"%s\"." + +#: lib/tools.js:145 +msgid "MX record not found for domain" +msgstr "Nie znaleziono rekordu MX dla domeny" + +#: lib/tools.js:148 +msgid "Address domain not found" +msgstr "Nie znaleziono domeny" + +#: lib/tools.js:151 +msgid "Address domain name is required" +msgstr "Nazwa domeny adresu jest wymagana" + +#: routes/archive.js:31 routes/archive.js:43 routes/archive.js:55 app.js:211 +msgid "Not Found" +msgstr "Nie znaleziono" + +#: routes/archive.js:110 services/sender.js:447 +msgid "Received status code %s from %s" +msgstr "Otrzymano kod statusu %s z %s" + +#: routes/archive.js:134 routes/campaigns.js:131 routes/campaigns.js:295 +#: routes/campaigns.js:390 routes/campaigns.js:435 routes/campaigns.js:475 +#: routes/campaigns.js:739 routes/campaigns.js:762 routes/campaigns.js:781 +#: routes/campaigns.js:803 routes/triggers.js:146 +msgid "Could not find campaign with specified ID" +msgstr "Nie można znaleźć kampanii z określonym ID" + +#: routes/archive.js:142 routes/campaigns.js:789 +msgid "Attachment not found" +msgstr "Załącznik nie znaleziony" + +#: routes/campaigns.js:26 routes/fields.js:13 routes/lists.js:49 +#: routes/segments.js:13 routes/settings.js:23 routes/templates.js:17 +#: routes/triggers.js:18 routes/users.js:75 routes/users.js:120 +msgid "Need to be logged in to access restricted content" +msgstr "Musisz być zalogowany, aby uzyskać dostęp do zastrzeżonych treści" + +#: routes/campaigns.js:117 +msgid "Could not create campaign" +msgstr "Nie można utworzyć kampanii" + +#: routes/campaigns.js:120 +msgid "Campaign “%s” created" +msgstr "Kampania “%s” utworzona" + +#: routes/campaigns.js:204 +msgid "content from an RSS entry" +msgstr "treść z wpisu RSS" + +#: routes/campaigns.js:220 +msgid "Campaign settings updated" +msgstr "Zaktualizowano ustawienia kampanii" + +#: routes/campaigns.js:222 +msgid "Campaign settings not updated" +msgstr "Ustawienia kampanii nie zostały zaktualizowane" + +#: routes/campaigns.js:238 routes/campaigns.js:639 +msgid "Campaign deleted" +msgstr "Kampania usunięta" + +#: routes/campaigns.js:240 routes/campaigns.js:641 +msgid "Could not delete specified campaign" +msgstr "Nie można usunąć określonej kampanii" + +#: routes/campaigns.js:259 +msgid "Idling" +msgstr "Bezczynność" + +#: routes/campaigns.js:262 +msgid "Scheduled" +msgstr "Planowane" + +#: routes/campaigns.js:268 +msgid "Paused" +msgstr "Wstrzymane" + +#: routes/campaigns.js:270 +msgid "Inactive" +msgstr "Nieaktywny" + +#: routes/campaigns.js:272 +msgid "Active" +msgstr "Aktywny" + +#: routes/campaigns.js:274 +msgid "Other" +msgstr "Inny" + +#: routes/campaigns.js:429 +msgid "Unknown status selector" +msgstr "Nieznany status selektora" + +#: routes/campaigns.js:657 +msgid "Scheduled sending" +msgstr "Wysyłanie zaplanowanych" + +#: routes/campaigns.js:659 +msgid "Could not schedule sending" +msgstr "Nie można zaplanować wysyłania" + +#: routes/campaigns.js:671 +msgid "Sending resumed" +msgstr "Wysyłanie zostało wznowione" + +#: routes/campaigns.js:673 +msgid "Could not resume sending" +msgstr "Nie można wznowić wysyłania" + +#: routes/campaigns.js:685 +msgid "Sending reset" +msgstr "Wysyłam reset" + +#: routes/campaigns.js:687 +msgid "Could not reset sending" +msgstr "Nie można zresetować wysyłania" + +#: routes/campaigns.js:699 routes/campaigns.js:727 +msgid "Sending paused" +msgstr "Wysyłanie wstrzymane" + +#: routes/campaigns.js:701 routes/campaigns.js:729 +msgid "Could not pause sending" +msgstr "Nie można wstrzymać wysyłania" + +#: routes/campaigns.js:713 +msgid "Sending activated" +msgstr "Wysyłanie aktywowane" + +#: routes/campaigns.js:715 +msgid "Could not activate sending" +msgstr "Nie można aktywować wysyłania" + +#: routes/campaigns.js:750 +msgid "Attachment uploaded" +msgstr "Przesłano załącznik" + +#: routes/campaigns.js:752 +msgid "Could not store attachment" +msgstr "Nie można zapisać załącznika" + +#: routes/campaigns.js:769 +msgid "Attachment deleted" +msgstr "Załącznik usunięty" + +#: routes/campaigns.js:771 +msgid "Could not delete attachment" +msgstr "Nie można usunąć załącznika" + +#: routes/fields.js:28 routes/fields.js:64 routes/fields.js:118 +#: 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 "Nie znaleziono wybranego ID listy" + +#: routes/fields.js:102 +msgid "Could not create custom field" +msgstr "Nie można utworzyć pola niestandardowego" + +#: routes/fields.js:129 +msgid "Selected field not found" +msgstr "Nie znaleziono wybranego pola" + +#: routes/fields.js:165 +msgid "Field settings updated" +msgstr "Zaktualizowano ustawienia pola" + +#: routes/fields.js:167 +msgid "Field settings not updated" +msgstr "Ustawienia pola nie zostały zaktualizowane" + +#: routes/fields.js:183 +msgid "Custom field deleted" +msgstr "Pole niestandardowe usunięte" + +#: routes/fields.js:185 +msgid "Could not delete specified field" +msgstr "Nie można usunąć określonego pola" + +#: routes/links.js:40 +msgid "Oops, we couldn't find a link for the URL you clicked" +msgstr "Ups, nie mogliśmy znaleźć linku dla URL który kliknąłeś" + +#: routes/lists.js:90 +msgid "Could not create list" +msgstr "Nie można utworzyć listy" + +#: routes/lists.js:93 +msgid "List created" +msgstr "Utworzono listę" + +#: routes/lists.js:101 routes/lists.js:236 routes/lists.js:301 +#: routes/lists.js:340 routes/lists.js:409 routes/lists.js:434 +#: routes/lists.js:479 routes/lists.js:501 routes/lists.js:530 +#: routes/lists.js:609 routes/lists.js:666 routes/lists.js:693 +msgid "Could not find list with specified ID" +msgstr "Nie można znaleźć listy o określonym ID" + +#: routes/lists.js:115 +msgid "List settings updated" +msgstr "Zaktualizowano ustawienia listy" + +#: routes/lists.js:117 +msgid "List settings not updated" +msgstr "Nie zaktualizowano ustawień listy" + +#: routes/lists.js:133 +msgid "List deleted" +msgstr "Lista usunięta" + +#: routes/lists.js:135 +msgid "Could not delete specified list" +msgstr "Nie można usunąć określonej listy" + +#: routes/lists.js:171 +msgid "Unknown" +msgstr "Nieznany" + +#: routes/lists.js:171 +msgid "Complained" +msgstr "Skarżący" + +#: routes/lists.js:202 +msgid "Invalid key" +msgstr "Klucz niewłaściwy" + +#: routes/lists.js:204 +msgid "Expired key" +msgstr "Klucz wygasł" + +#: routes/lists.js:206 +msgid "Revoked key" +msgstr "Klucz odwołany" + +#: routes/lists.js:256 +msgid "Initializing" +msgstr "Inicjowanie" + +#: routes/lists.js:259 +msgid "Initialized" +msgstr "Zainicjowano" + +#: routes/lists.js:262 +msgid "Importing" +msgstr "Importowanie" + +#: routes/lists.js:268 +msgid "Errored" +msgstr "Błędne" + +#: routes/lists.js:346 routes/lists.js:415 routes/lists.js:440 +msgid "Could not find subscriber with specified ID" +msgstr "Nie można znaleźć subskrybenta o podanym ID" + +#: routes/lists.js:392 +msgid "Could not add subscription" +msgstr "Nie można dodać subskrypcji" + +#: routes/lists.js:397 +msgid "%s was successfully added to your list" +msgstr "%s został pomyślnie dodany do twojej listy" + +#: routes/lists.js:399 +msgid "%s was not added to your list" +msgstr "%s nie został dodany do Twojej listy" + +#: routes/lists.js:421 +msgid "Could not unsubscribe user" +msgstr "Nie można anulować subskrypcji użytkownika" + +#: routes/lists.js:424 +msgid "%s was successfully unsubscribed from your list" +msgstr "%s został pomyślnie wypisany z listy" + +#: routes/lists.js:444 +msgid "%s was successfully removed from your list" +msgstr "%s został pomyślnie usunięty z Twojej listy" + +#: routes/lists.js:456 +msgid "Another subscriber with email address %s already exists" +msgstr "Inny subskrybent z adresem e-mail %s już istnieje" + +#: routes/lists.js:463 +msgid "Subscription settings updated" +msgstr "Zaktualizowano ustawienia subskrypcji" + +#: routes/lists.js:465 +msgid "Subscription settings not updated" +msgstr "Ustawienia subskrypcji nie zostały zaktualizowane" + +#: routes/lists.js:507 routes/lists.js:615 routes/lists.js:651 +#: routes/lists.js:679 routes/lists.js:699 +msgid "Could not find import data with specified ID" +msgstr "Nie można znaleźć danych importu o podanym ID" + +#: routes/lists.js:538 +msgid "Could not process CSV" +msgstr "Nie można przetworzyć pliku CSV" + +#: routes/lists.js:547 +msgid "Could not create importer" +msgstr "Nie można utworzyć importera" + +#: routes/lists.js:598 +msgid "Empty file" +msgstr "Pusty plik" + +#: routes/lists.js:655 +msgid "Import started" +msgstr "Rozpoczęto importowanie" + +#: routes/lists.js:683 +msgid "Import restarted" +msgstr "Import został zrestartowany" + +#: routes/segments.js:86 +msgid "Could not create segment" +msgstr "Nie można utworzyć segmentu" + +#: routes/segments.js:89 +msgid "Segment created" +msgstr "Utworzono segment" + +#: routes/segments.js:113 +msgid "Selected segment ID not found" +msgstr "Nie znaleziono ID wybranego segmentu" + +#: routes/segments.js:188 +msgid "Segment settings updated" +msgstr "Zaktualizowane ustawienia segmentów" + +#: routes/segments.js:190 +msgid "Segment settings not updated" +msgstr "Ustawienia segmentu nie zostały zaktualizowane" + +#: routes/segments.js:206 +msgid "Segment deleted" +msgstr "Segment usunięty" + +#: routes/segments.js:208 +msgid "Could not delete specified segment" +msgstr "Nie można usunąć określonego segmentu" + +#: routes/segments.js:342 +msgid "Could not create rule" +msgstr "Nie można utworzyć reguły" + +#: routes/segments.js:345 +msgid "Rule created" +msgstr "Dodano regułę" + +#: routes/segments.js:410 +msgid "Rule settings updated" +msgstr "Zaktualizowano ustawienia reguły" + +#: routes/segments.js:412 +msgid "Rule settings not updated" +msgstr "Nie zaktualizowano ustawień reguły" + +#: routes/segments.js:428 +msgid "Rule deleted" +msgstr "Usunięto regułę" + +#: routes/segments.js:430 +msgid "Could not delete specified rule" +msgstr "Nie można usunąć określonej reguły" + +#: routes/settings.js:39 +msgid "Use TLS" +msgstr "Użyj TLS" + +#: routes/settings.js:40 +msgid "usually selected for port 465" +msgstr "zazwyczaj wybierany port 465" + +#: routes/settings.js:44 +msgid "Use STARTTLS" +msgstr "Użyj STARTTLS" + +#: routes/settings.js:45 +msgid "usually selected for port 587 and 25" +msgstr "zazwyczaj wybierany port 587 i 25" + +#: routes/settings.js:49 +msgid "Do not use encryption" +msgstr "Nie używaj szyfrowania" + +#: routes/settings.js:115 +msgid "Settings updated" +msgstr "Zaktualizowano ustawienia" + +#: routes/settings.js:173 +msgid "Invalid mail transport type" +msgstr "Nieprawidłowy typ transportu poczty" + +#: routes/settings.js:184 +msgid "Invalid Access Key" +msgstr "Nieprawidłowy klucz dostępu" + +#: routes/settings.js:187 +msgid "Invalid AWS credentials" +msgstr "Nieprawidłowe dane uwierzytelniające AWS" + +#: routes/settings.js:190 +msgid "Connection refused, check hostname and port." +msgstr "Połączenie odrzucone, sprawdź nazwę hosta i port." + +#: 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 "" +"Nie otrzymano wiadomości powitalnej z serwera. Może się to zdarzyć podczas " +"łączenia się z portem TLS bez korzystania z TLS." + +#: routes/settings.js:197 +msgid "Did not receive greeting message from server." +msgstr "Nie otrzymano wiadomości powitalnej z serwera." + +#: routes/settings.js:200 +msgid "" +"Connection timed out. Check your firewall settings, destination port is " +"probably blocked." +msgstr "" +"Przekroczono limit czasu połączenia. Sprawdź ustawienia zapory, port " +"docelowy jest prawdopodobnie zablokowany." + +#: routes/settings.js:205 +msgid "Authentication not accepted, server expects STARTTLS to be used." +msgstr "" +"Uwierzytelnianie nie jest akceptowane, serwer oczekuje, że zostanie użyty " +"STARTTLS." + +#: routes/settings.js:207 +msgid "Authentication failed, check username and password." +msgstr "Uwierzytelnianie nie powiodło się, sprawdź nazwę użytkownika i hasło." + +#: routes/settings.js:217 +msgid "Failed Mailer verification." +msgstr "Weryfikacja Mailera nie powiodła się." + +#: routes/settings.js:217 +msgid "Server responded with: \"%s\"" +msgstr "Odpowiedź serwera: \"%s\"" + +#: routes/settings.js:221 +msgid "Mailer settings verified, ready to send some mail!" +msgstr "Ustawienia Mailera zweryfikowane, gotowe do wysłania wiadomości!" + +#: routes/subscription.js:22 +msgid "Selected subscription not found" +msgstr "Nie znaleziono wybranej subskrypcji" + +#: routes/subscription.js:32 routes/subscription.js:103 +#: routes/subscription.js:141 routes/subscription.js:166 +#: routes/subscription.js:191 routes/subscription.js:232 +#: routes/subscription.js:270 routes/subscription.js:317 +#: routes/subscription.js:339 routes/subscription.js:368 +#: routes/subscription.js:392 routes/subscription.js:424 +msgid "Selected list not found" +msgstr "Nie znaleziono wybranej listy" + +#: routes/subscription.js:78 routes/subscription.js:472 +msgid "%s: Subscription Confirmed" +msgstr "%s: Potwierdzono subskrypcję" + +#: routes/subscription.js:217 +msgid "Email address not set" +msgstr "Adres e-mail nie jest ustawiony" + +#: routes/subscription.js:255 +msgid "Could not store confirmation data" +msgstr "Nie można zapisać danych potwierdzających" + +#: routes/subscription.js:284 routes/subscription.js:349 +#: routes/subscription.js:402 +msgid "Subscription not found from this list" +msgstr "Nie znaleziono subskrypcji z tej listy" + +#: routes/subscription.js:383 +msgid "Email address updated, check your mailbox for verification instructions" +msgstr "" +"Adres e-mail zaktualizowany, sprawdź skrzynkę pocztową, aby uzyskać " +"instrukcje dotyczące weryfikacji" + +#: routes/subscription.js:499 routes/subscription.js:515 +msgid "Public key is not set" +msgstr "Klucz publiczny nie jest ustawiony" + +#: routes/templates.js:98 +msgid "Could not create template" +msgstr "Nie można utworzyć szablonu" + +#: routes/templates.js:101 +msgid "Template created" +msgstr "Szablon utworzony" + +#: routes/templates.js:109 +msgid "Could not find template with specified ID" +msgstr "Nie można znaleźć szablonu z określonym ID" + +#: routes/templates.js:140 +msgid "Template settings updated" +msgstr "Zaktualizowano ustawienia szablonu" + +#: routes/templates.js:142 +msgid "Template settings not updated" +msgstr "Ustawienia szablonów nie zostały zaktualizowane" + +#: routes/templates.js:158 +msgid "Template deleted" +msgstr "Szablon usunięty" + +#: routes/templates.js:160 +msgid "Could not delete specified template" +msgstr "Nie można usunąć określonego szablonu" + +#: routes/triggers.js:62 routes/triggers.js:79 routes/triggers.js:154 +msgid "Could not find selected list" +msgstr "Nie można znaleźć wybranej listy" + +#: routes/triggers.js:131 +msgid "Could not create trigger" +msgstr "Nie można utworzyć wyzwalacza" + +#: routes/triggers.js:138 +msgid "Trigger “%s” created" +msgstr "Wyzwalacz “%s” został utworzony" + +#: routes/triggers.js:214 +msgid "Trigger settings updated" +msgstr "Zaktualizowano ustawienia wyzwalacza" + +#: routes/triggers.js:216 +msgid "Trigger settings not updated" +msgstr "Ustawienia wyzwalacza nie zostały zaktualizowane" + +#: routes/triggers.js:228 +msgid "Trigger deleted" +msgstr "Wyzwalacz został usunięty" + +#: routes/triggers.js:230 +msgid "Could not delete specified trigger" +msgstr "Nie można usunąć określonego wyzwalacza" + +#: routes/triggers.js:242 +msgid "Could not find trigger with specified ID" +msgstr "Nie można znaleźć wyzwalacza z określonym ID" + +#: routes/triggers.js:255 +msgid "Trigger not found" +msgstr "Wyzwalacz nie został znaleziony" + +#: 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 "" +"E-mail z instrukcjami resetowania hasła został wysłany na Twój adres e-mail, " +"jeśli istnieje w naszym systemie." + +#: routes/users.js:46 routes/users.js:64 +msgid "Unknown or expired reset token" +msgstr "Token resetowania nieznany lub wygasł" + +#: routes/users.js:66 +msgid "Your password has been changed successfully" +msgstr "Twoje hasło zostało zmienione" + +#: routes/users.js:87 +msgid "User data not found" +msgstr "Nie znaleziono danych użytkownika" + +#: routes/users.js:110 +msgid "Access token updated" +msgstr "Zaktualizowano token dostępu" + +#: routes/users.js:112 +msgid "Access token not updated" +msgstr "Nie zaktualizowano tokenu dostępu" + +#: routes/users.js:139 +msgid "Account information updated" +msgstr "Informacje o koncie zostały zaktualizowane" + +#: routes/users.js:141 +msgid "Account information not updated" +msgstr "Informacje o koncie nie zostały zaktualizowane" + +#: services/feedcheck.js:51 +msgid "Feed error: %s" +msgstr "RSS błąd: %s" + +#: services/feedcheck.js:54 +msgid "Found %s new campaign messages from feed" +msgstr "Znaleziono %s nowych wiadomości kampanii z kanału" + +#: services/feedcheck.js:56 +msgid "Found nothing new from the feed" +msgstr "Nie znaleziono nic nowego w kanale" + +#: services/feedcheck.js:143 +msgid "RSS entry %s" +msgstr "Wpis RSS %s" + +#: services/importer.js:243 +msgid "Could not access import file" +msgstr "Nie można uzyskać dostępu do pliku importu" + +#: services/triggers.js:51 +msgid "Unknown trigger type %s" +msgstr "Nieznany typ wyzwalacza %s" + +#: views/subscription/widget-subscribe.hbs:3 +msgid "It looks like you are already subscribed to this list." +msgstr "Wygląda na to, że już jesteś zapisany/a." diff --git a/lib/db.js b/lib/db.js index 2c3a7b21..44f17b0f 100644 --- a/lib/db.js +++ b/lib/db.js @@ -2,5 +2,113 @@ let config = require('config'); let mysql = require('mysql'); +let redis = require('redis'); +let Lock = require('redfour'); +let stringifyDate = require('json-stringify-date'); +let tools = require('./tools'); module.exports = mysql.createPool(config.mysql); +if (config.redis && config.redis.enabled) { + + module.exports.redis = redis.createClient(config.redis); + + let queueLock = new Lock({ + redis: config.redis, + namespace: 'mailtrain:lock' + }); + + module.exports.getLock = (id, callback) => { + queueLock.waitAcquireLock(id, 60 * 1000 /* Lock expires after 60sec */ , 10 * 1000 /* Wait for lock for up to 10sec */ , (err, lock) => { + if (err) { + return callback(err); + } + if (!lock) { + return callback(null, false); + } + return callback(null, { + lock, + release(done) { + queueLock.releaseLock(lock, done); + } + }); + }); + }; + + module.exports.clearCache = (key, callback) => { + module.exports.redis.del('mailtrain:cache:' + key, err => callback(err)); + }; + + module.exports.addToCache = (key, value, callback) => { + if (!value) { + return setImmediate(() => callback()); + } + module.exports.redis.multi(). + lpush('mailtrain:cache:' + key, stringifyDate.stringify(value)). + expire('mailtrain:cache:' + key, 24 * 3600). + exec(err => callback(err)); + }; + + module.exports.getFromCache = (key, callback) => { + module.exports.redis.rpop('mailtrain:cache:' + key, (err, value) => { + if (err) { + return callback(err); + } + try { + value = stringifyDate.parse(value); + } catch (E) { + return callback(E); + } + + return callback(null, value); + }); + }; + +} else { + // fakelock. does not lock anything + module.exports.getLock = (id, callback) => { + setImmediate(() => callback(null, { + lock: false, + release(done) { + setImmediate(done); + } + })); + }; + + let caches = new Map(); + + module.exports.clearCache = (key, callback) => { + caches.delete(key); + tools.workers.forEach(child => { + child.send({ + cmd: 'db.clearCache', + key + }); + }); + setImmediate(() => callback()); + }; + + process.on('message', m => { + if (m && m.cmd === 'db.clearCache' && m.key) { + caches.delete(m.key); + } + }); + + module.exports.addToCache = (key, value, callback) => { + if (!caches.has(key)) { + caches.set(key, []); + } + caches.get(key).push(value); + setImmediate(() => callback()); + }; + + module.exports.getFromCache = (key, callback) => { + let value; + if (caches.has(key)) { + value = caches.get(key).shift(); + if (!caches.get(key).length) { + caches.delete(key); + } + } + setImmediate(() => callback(null, value)); + }; +} diff --git a/lib/dbcheck.js b/lib/dbcheck.js index 30b13b0c..93264800 100644 --- a/lib/dbcheck.js +++ b/lib/dbcheck.js @@ -21,10 +21,10 @@ let db = mysql.createPool(mysqlConfig); function listTables(callback) { db.getConnection((err, connection) => { if (err) { - if(err.code === 'ER_ACCESS_DENIED_ERROR'){ + if (err.code === 'ER_ACCESS_DENIED_ERROR') { err = new Error('Could not access the database. Check MySQL config and authentication credentials'); } - if(err.code === 'ECONNREFUSED' || err.code === 'PROTOCOL_SEQUENCE_TIMEOUT'){ + if (err.code === 'ECONNREFUSED' || err.code === 'PROTOCOL_SEQUENCE_TIMEOUT') { err = new Error('Could not connect to the database. Check MySQL host and port configuration'); } return callback(err); @@ -107,13 +107,14 @@ function getSql(path, data, callback) { if (err) { return callback(err); } - let renderer = Handlebars.compile(source); - return callback(null, renderer(data || {})); + const rendered = data ? Handlebars.compile(source)(data) : source; + return callback(null, rendered); }); } function runInitial(callback) { - let fname = process.env.DB_FROM_START ? 'base.sql' : 'mailtrain.sql'; + let dump = process.env.NODE_ENV === 'test' ? 'mailtrain-test.sql' : 'mailtrain.sql'; + let fname = process.env.DB_FROM_START ? 'base.sql' : dump; let path = pathlib.join(__dirname, '..', 'setup', 'sql', fname); log.info('sql', 'Loading tables from %s', fname); applyUpdate({ diff --git a/lib/editor-helpers.js b/lib/editor-helpers.js new file mode 100644 index 00000000..e4c4e79f --- /dev/null +++ b/lib/editor-helpers.js @@ -0,0 +1,79 @@ +'use strict'; + +let _ = require('../lib/translate')._; +let helpers = require('../lib/helpers'); +let templates = require('../lib/models/templates'); +let campaigns = require('../lib/models/campaigns'); + +module.exports = { + getResource, + getMergeTagsForResource +}; + +function getResource(type, id, callback) { + if (type === 'template') { + templates.get(id, (err, template) => { + if (err || !template) { + return callback(err && err.message || err || _('Could not find template with specified ID')); + } + + getMergeTagsForResource(template, (err, mergeTags) => { + if (err) { + return callback(err.message || err); + } + + template.mergeTags = mergeTags; + return callback(null, template); + }); + }); + + } else if (type === 'campaign') { + campaigns.get(id, false, (err, campaign) => { + if (err || !campaign) { + return callback(err && err.message || err || _('Could not find campaign with specified ID')); + } + + getMergeTagsForResource(campaign, (err, mergeTags) => { + if (err) { + return callback(err.message || err); + } + + campaign.mergeTags = mergeTags; + return callback(null, campaign); + }); + }); + + } else { + return callback(_('Invalid resource type')); + } +} + +function getMergeTagsForResource(resource, callback) { + helpers.getDefaultMergeTags((err, defaultMergeTags) => { + if (err) { + return callback(err.message || err); + } + + if (!Number(resource.list)) { + return callback(null, defaultMergeTags); + } + + helpers.getListMergeTags(resource.list, (err, listMergeTags) => { + if (err) { + return callback(err.message || err); + } + + if (resource.type !== 2) { + return callback(null, defaultMergeTags.concat(listMergeTags)); + } + + helpers.getRSSMergeTags((err, rssMergeTags) => { + if (err) { + return callback(err.message || err); + } + + callback(null, defaultMergeTags.concat(listMergeTags, rssMergeTags)); + }); + }); + }); +} diff --git a/lib/executor.js b/lib/executor.js new file mode 100644 index 00000000..1cf1d959 --- /dev/null +++ b/lib/executor.js @@ -0,0 +1,83 @@ +'use strict'; + +const fork = require('child_process').fork; +const log = require('npmlog'); +const path = require('path'); + +const requestCallbacks = {}; +let messageTid = 0; +let executorProcess; + +module.exports = { + spawn, + start, + stop +}; + +function spawn(callback) { + log.info('Executor', 'Spawning executor process.'); + + executorProcess = fork(path.join(__dirname, '..', 'services', 'executor.js'), [], { + cwd: path.join(__dirname, '..'), + env: {NODE_ENV: process.env.NODE_ENV} + }); + + executorProcess.on('message', msg => { + if (msg) { + if (msg.type === 'process-started') { + let requestCallback = requestCallbacks[msg.tid]; + if (requestCallback && requestCallback.startedCallback) { + requestCallback.startedCallback(msg.tid); + } + + } else if (msg.type === 'process-failed') { + let requestCallback = requestCallbacks[msg.tid]; + if (requestCallback && requestCallback.failedCallback) { + requestCallback.failedCallback(msg.msg); + } + + delete requestCallbacks[msg.tid]; + + } else if (msg.type === 'process-finished') { + let requestCallback = requestCallbacks[msg.tid]; + if (requestCallback && requestCallback.startedCallback) { + requestCallback.finishedCallback(msg.code, msg.signal); + } + + delete requestCallbacks[msg.tid]; + + } else if (msg.type === 'executor-started') { + log.info('Executor', 'Executor process started.'); + return callback(); + } + } + }); + + executorProcess.on('close', (code, signal) => { + log.info('Executor', 'Executor process exited with code %s signal %s.', code, signal); + }); +} + +function start(type, data, startedCallback, finishedCallback, failedCallback) { + requestCallbacks[messageTid] = { + startedCallback, + finishedCallback, + failedCallback + }; + + executorProcess.send({ + type: 'start-' + type, + data, + tid: messageTid + }); + + messageTid++; +} + +function stop(tid) { + executorProcess.send({ + type: 'stop-process', + tid + }); +} + diff --git a/lib/fakelang.js b/lib/fakelang.js new file mode 100644 index 00000000..7a6027a1 --- /dev/null +++ b/lib/fakelang.js @@ -0,0 +1,26 @@ +'use strict'; + +/* lloyd|2012|http://wtfpl.org */ + +/* eslint-disable */ + +module.exports = str => { + let from = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+\\|`~[{]};:'\",<.>/?"; + let to = "ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z0123456789¡@#$%ᵥ⅋⁎()-_=+\\|,~[{]};:,„´<.>/¿"; + + return str.replace(/(\{\{[^\}]+\}\}|%s)/g, '\x00\x04$1\x00').split('\x00').map(c => { + if (c.charAt(0) === '\x04') { + return c; + } + let r = ''; + for (let i = 0, len = c.length; i < len; i++) { + let pos = from.indexOf(c.charAt(i)); + if (pos < 0) { + r += c.charAt(i); + } else { + r += to.charAt(pos); + } + } + return r; + }).join('\x00').replace(/[\x00\x04]/g, ''); +} diff --git a/lib/feed.js b/lib/feed.js new file mode 100644 index 00000000..66bd2f20 --- /dev/null +++ b/lib/feed.js @@ -0,0 +1,68 @@ +'use strict'; + +let FeedParser = require('feedparser'); +let request = require('request'); +let _ = require('./translate')._; +let util = require('util'); + +module.exports.fetch = (url, callback) => { + let req = request(url); + let feedparser = new FeedParser(); + let returned = false; + let entries = []; + + req.setHeader('user-agent', 'Mailtrain'); + req.setHeader('accept', 'text/html,application/xhtml+xml'); + + req.on('error', err => { + if (returned) { + return; + } + returned = true; + callback(err); + }); + + req.on('response', res => { + if (returned) { + return; + } + + if (res.statusCode !== 200) { + return req.emit('error', new Error(util.format(_('Bad status code %s'), res.statusCode))); + } + + req.pipe(feedparser); + }); + + feedparser.on('error', err => { + if (returned) { + return; + } + returned = true; + callback(err); + }); + + feedparser.on('readable', () => { + let item; + while ((item = feedparser.read())) { + let entry = { + title: item.title, + date: item.date || item.pubdate || item.pubDate || new Date(), + guid: item.guid || item.link, + link: item.link, + content: item.description || item.summary, + summary: item.summary || item.description, + image_url: item.image.url + }; + entries.push(entry); + } + }); + + feedparser.on('end', () => { + if (returned) { + return; + } + returned = true; + callback(null, entries); + }); +}; diff --git a/lib/file-helpers.js b/lib/file-helpers.js new file mode 100644 index 00000000..deee31e1 --- /dev/null +++ b/lib/file-helpers.js @@ -0,0 +1,32 @@ +'use strict'; + +const path = require('path'); + +function nameToFileName(name) { + return name. + trim(). + toLowerCase(). + replace(/[ .+/]/g, '-'). + replace(/[^a-z0-9\-_]/gi, ''). + replace(/--*/g, '-'); +} + + +function getReportFileBase(report) { + return path.join(__dirname, '..', 'protected', 'reports', report.id + '-' + nameToFileName(report.name)); +} + +function getReportContentFile(report) { + return getReportFileBase(report) + '.out'; +} + +function getReportOutputFile(report) { + return getReportFileBase(report) + '.err'; +} + + +module.exports = { + getReportContentFile, + getReportOutputFile, + nameToFileName +}; diff --git a/lib/handlebars-helpers.js b/lib/handlebars-helpers.js new file mode 100644 index 00000000..10a38741 --- /dev/null +++ b/lib/handlebars-helpers.js @@ -0,0 +1,49 @@ +'use strict'; + +const util = require('util'); + +const _ = require('../lib/translate')._; + +module.exports.registerHelpers = handlebars => { + // {{#translate}}abc{{/translate}} + handlebars.registerHelper('translate', function (context, options) { // eslint-disable-line prefer-arrow-callback + if (typeof options === 'undefined' && context) { + options = context; + context = false; + } + + let result = _(options.fn(this)); // eslint-disable-line no-invalid-this + + if (Array.isArray(context)) { + result = util.format(result, ...context); + } + return new handlebars.SafeString(result); + }); + + + /* Credits to http://chrismontrois.net/2016/01/30/handlebars-switch/ + + {{#switch letter}} + {{#case "a"}} + A is for alpaca + {{/case}} + {{#case "b"}} + B is for bluebird + {{/case}} + {{/switch}} + */ + /* eslint no-invalid-this: "off" */ + handlebars.registerHelper('switch', function(value, options) { + this._switch_value_ = value; + const html = options.fn(this); // Process the body of the switch block + delete this._switch_value_; + return html; + }); + + handlebars.registerHelper('case', function(value, options) { + if (value === this._switch_value_) { + return options.fn(this); + } + }); + +}; diff --git a/lib/helpers.js b/lib/helpers.js new file mode 100644 index 00000000..786e3473 --- /dev/null +++ b/lib/helpers.js @@ -0,0 +1,295 @@ +'use strict'; + +let config = require('config'); +let path = require('path'); +let fs = require('fs'); +let tools = require('./tools'); +let settings = require('./models/settings'); +let lists = require('./models/lists'); +let fields = require('./models/fields'); +let forms = require('./models/forms'); +let _ = require('./translate')._; +let objectHash = require('object-hash'); +let mjml = require('mjml'); +let mjmlTemplates = new Map(); +let hbs = require('hbs'); + +module.exports = { + getDefaultMergeTags, + getRSSMergeTags, + getListMergeTags, + captureFlashMessages, + injectCustomFormData, + injectCustomFormTemplates, + filterCustomFields, + getMjmlTemplate, + rollbackAndReleaseConnection +}; + +function getDefaultMergeTags(callback) { + // Using a callback for the sake of future-proofness + callback(null, [{ + key: 'LINK_UNSUBSCRIBE', + value: _('URL that points to the unsubscribe page') + }, { + key: 'LINK_PREFERENCES', + value: _('URL that points to the preferences page of the subscriber') + }, { + key: 'LINK_BROWSER', + value: _('URL to preview the message in a browser') + }, { + key: 'EMAIL', + value: _('Email address') + }, { + key: 'FIRST_NAME', + value: _('First name') + }, { + key: 'LAST_NAME', + value: _('Last name') + }, { + key: 'FULL_NAME', + value: _('Full name (first and last name combined)') + }, { + key: 'SUBSCRIPTION_ID', + value: _('Unique ID that identifies the recipient') + }, { + key: 'LIST_ID', + value: _('Unique ID that identifies the list used for this campaign') + }, { + key: 'CAMPAIGN_ID', + value: _('Unique ID that identifies current campaign') + }]); +} + +function getRSSMergeTags(callback) { + // Using a callback for the sake of future-proofness + callback(null, [{ + key: 'RSS_ENTRY', + value: _('content from an RSS entry') + }, { + key: 'RSS_ENTRY_TITLE', + value: _('RSS entry title') + }, { + key: 'RSS_ENTRY_DATE', + value: _('RSS entry date') + }, { + key: 'RSS_ENTRY_LINK', + value: _('RSS entry link') + }, { + key: 'RSS_ENTRY_CONTENT', + value: _('content from an RSS entry') + }, { + key: 'RSS_ENTRY_SUMMARY', + value: _('RSS entry summary') + }, { + key: 'RSS_ENTRY_IMAGE_URL', + value: _('RSS entry image URL') + }]); +} + +function getListMergeTags(listId, callback) { + lists.get(listId, (err, list) => { + if (err) { + return callback(err); + } + if (!list) { + list = { + id: listId + }; + } + + fields.list(list.id, (err, fieldList) => { + if (err && !fieldList) { + fieldList = []; + } + + let mergeTags = []; + + fieldList.forEach(field => { + mergeTags.push({ + key: field.key, + value: field.name + }); + }); + + return callback(null, mergeTags); + }); + }); +} + +function filterCustomFields(customFieldsIn = [], fieldIds = [], method = 'include') { + let customFields = customFieldsIn.slice(); + fieldIds = typeof fieldIds === 'string' ? fieldIds.split(',') : fieldIds; + + customFields.unshift({ + id: 'email', + name: 'Email Address', + type: 'Email', + typeSubscriptionEmail: true + }, { + id: 'firstname', + name: 'First Name', + type: 'Text', + typeFirstName: true + }, { + id: 'lastname', + name: 'Last Name', + type: 'Text', + typeLastName: true + }); + + let filtered = []; + + if (method === 'include') { + fieldIds.forEach(id => { + let field = customFields.find(f => f.id.toString() === id); + field && filtered.push(field); + }); + } else { + customFields.forEach(field => { + !fieldIds.includes(field.id.toString()) && filtered.push(field); + }); + } + + return filtered; +} + +function injectCustomFormData(customFormId, viewPath, data, callback) { + + let injectDefaultData = data => { + data.customFields = filterCustomFields(data.customFields, [], 'exclude'); + data.formInputStyle = '@import url(/subscription/form-input-style.css);'; + return data; + }; + + if (Number(customFormId) < 1) { + return callback(null, injectDefaultData(data)); + } + + forms.get(customFormId, (err, form) => { + if (err) { + return callback(null, injectDefaultData(data)); + } + + let view = viewPath.split('/')[1]; + + if (view === 'web-subscribe') { + data.customFields = form.fieldsShownOnSubscribe + ? filterCustomFields(data.customFields, form.fieldsShownOnSubscribe) + : filterCustomFields(data.customFields, [], 'exclude'); + } else if (view === 'web-manage') { + data.customFields = form.fieldsShownOnManage + ? filterCustomFields(data.customFields, form.fieldsShownOnManage) + : filterCustomFields(data.customFields, [], 'exclude'); + } + + let key = tools.fromDbKey(view); + data.template.template = form[key] || data.template.template; + data.template.layout = form.layout || data.template.layout; + data.formInputStyle = form.formInputStyle || '@import url(/subscription/form-input-style.css);'; + + settings.list(['ua_code'], (err, configItems) => { + if (err) { + return callback(err); + } + + data.uaCode = configItems.uaCode; + data.customSubscriptionScripts = config.customsubscriptionscripts || []; + callback(null, data); + }); + }); +} + +function injectCustomFormTemplates(customFormId, templates, callback) { + if (Number(customFormId) < 1) { + return callback(null, templates); + } + + forms.get(customFormId, (err, form) => { + if (err) { + return callback(null, templates); + } + + let lookUp = name => { + let key = tools.fromDbKey( + /subscription\/([^.]*)/.exec(name)[1] + ); + return form[key] || name; + }; + + Object.keys(templates).forEach(key => { + let value = templates[key]; + + if (typeof value === 'string') { + templates[key] = lookUp(value); + } + if (typeof value === 'object' && value.template) { + templates[key].template = lookUp(value.template); + } + if (typeof value === 'object' && value.layout) { + templates[key].layout = lookUp(value.layout); + } + }); + + callback(null, templates); + }); +} + +function getMjmlTemplate(template, callback) { + if (!template) { + return callback(null, false); + } + + let key = (typeof template === 'object') ? objectHash(template) : template; + + if (mjmlTemplates.has(key)) { + return callback(null, mjmlTemplates.get(key)); + } + + let done = source => { + let compiled; + try { + compiled = mjml.mjml2html(source); + } catch (err) { + return callback(err); + } + if (compiled.errors.length) { + return callback(compiled.errors[0].message || compiled.errors[0]); + } + let renderer = hbs.handlebars.compile(compiled.html); + mjmlTemplates.set(key, renderer); + callback(null, renderer); + }; + + if (typeof template === 'object') { + tools.mergeTemplateIntoLayout(template.template, template.layout, (err, source) => { + if (err) { + return callback(err); + } + done(source); + }); + } else { + fs.readFile(path.join(__dirname, '..', 'views', template), 'utf-8', (err, source) => { + if (err) { + return callback(err); + } + done(source); + }); + } +} + +function captureFlashMessages(req, res, callback) { + res.render('subscription/capture-flash-messages', { layout: null }, (err, flash) => { + if (err) { + return callback(err); + } + callback(null, flash); + }); +} + +function rollbackAndReleaseConnection(connection, callback) { + connection.rollback(() => { + connection.release(); + return callback(); + }); +} diff --git a/lib/mailer.js b/lib/mailer.js index 9ac3f861..4ef5ad0a 100644 --- a/lib/mailer.js +++ b/lib/mailer.js @@ -1,15 +1,37 @@ 'use strict'; let log = require('npmlog'); - +let config = require('config'); let nodemailer = require('nodemailer'); let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt; let settings = require('./models/settings'); +let tools = require('./tools'); +let db = require('./db'); let Handlebars = require('handlebars'); let fs = require('fs'); let path = require('path'); let templates = new Map(); let htmlToText = require('html-to-text'); +let aws = require('aws-sdk'); +let objectHash = require('object-hash'); +let mjml = require('mjml'); + +let _ = require('./translate')._; +let util = require('util'); + +Handlebars.registerHelper('translate', function (context, options) { // eslint-disable-line prefer-arrow-callback + if (typeof options === 'undefined' && context) { + options = context; + context = false; + } + + let result = _(options.fn(this)); // eslint-disable-line no-invalid-this + + if (Array.isArray(context)) { + result = util.format(result, ...context); + } + return new Handlebars.SafeString(result); +}); module.exports.transport = false; @@ -39,6 +61,11 @@ module.exports.sendMail = (mail, template, callback) => { }); } + if (!mail.headers) { + mail.headers = {}; + } + mail.headers['X-Sending-Zone'] = 'transactional'; + getTemplate(template.html, (err, htmlRenderer) => { if (err) { return callback(err); @@ -48,20 +75,47 @@ module.exports.sendMail = (mail, template, callback) => { mail.html = htmlRenderer(template.data || {}); } - getTemplate(template.text, (err, textRenderer) => { + tools.prepareHtml(mail.html, (err, prepareHtml) => { if (err) { - return callback(err); + // ignore } - if (textRenderer) { - mail.text = textRenderer(template.data || {}); - } else if (mail.html) { - mail.text = htmlToText.fromString(mail.html, { - wordwrap: 130 - }); + if (prepareHtml) { + mail.html = prepareHtml; } - module.exports.transport.sendMail(mail, callback); + getTemplate(template.text, (err, textRenderer) => { + if (err) { + return callback(err); + } + + if (textRenderer) { + mail.text = textRenderer(template.data || {}); + } else if (mail.html) { + mail.text = htmlToText.fromString(mail.html, { + wordwrap: 130 + }); + } + + let tryCount = 0; + let trySend = () => { + tryCount++; + + module.exports.transport.sendMail(mail, (err, info) => { + if (err) { + log.error('Mail', err); + if (err.responseCode && err.responseCode >= 400 && err.responseCode < 500 && tryCount <= 5) { + // temporary error, try again + log.verbose('Mail', 'Retrying after %s sec. ...', tryCount); + return setTimeout(trySend, tryCount * 1000); + } + return callback(err); + } + return callback(null, info); + }); + }; + setImmediate(trySend); + }); }); }); @@ -72,53 +126,171 @@ function getTemplate(template, callback) { return callback(null, false); } - if (templates.has(template)) { - return callback(null, templates.get(template)); + let key = (typeof template === 'object') ? objectHash(template) : template; + + if (templates.has(key)) { + return callback(null, templates.get(key)); } - fs.readFile(path.join(__dirname, '..', 'views', template), 'utf-8', (err, source) => { - if (err) { - return callback(err); + let done = (source, isMjml = false) => { + if (isMjml) { + let compiled; + try { + compiled = mjml.mjml2html(source); + } catch (err) { + return callback(err); + } + if (compiled.errors.length) { + return callback(compiled.errors[0].message || compiled.errors[0]); + } + source = compiled.html; } let renderer = Handlebars.compile(source); - templates.set(template, renderer); - return callback(null, renderer); - }); + templates.set(key, renderer); + callback(null, renderer); + }; + + if (typeof template === 'object') { + tools.mergeTemplateIntoLayout(template.template, template.layout, (err, source) => { + if (err) { + return callback(err); + } + let isMjml = template.type === 'mjml'; + done(source, isMjml); + }); + } else { + fs.readFile(path.join(__dirname, '..', 'views', template), 'utf-8', (err, source) => { + if (err) { + return callback(err); + } + done(source); + }); + } } function createMailer(callback) { - settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase'], (err, configItems) => { + settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase', 'smtpThrottling', 'mailTransport', 'sesKey', 'sesSecret', 'sesRegion'], (err, configItems) => { if (err) { return callback(err); } - module.exports.transport = nodemailer.createTransport({ - pool: true, - host: configItems.smtpHostname, - port: Number(configItems.smtpPort) || false, - secure: configItems.smtpEncryption === 'TLS', - ignoreTLS: configItems.smtpEncryption === 'NONE', - auth: configItems.smtpDisableAuth ? false : { - user: configItems.smtpUser, - pass: configItems.smtpPass - }, - debug: !!configItems.smtpLog, - logger: !configItems.smtpLog ? false : { - debug: log.info.bind(log, 'Mail'), - info: log.verbose.bind(log, 'Mail'), - error: log.info.bind(log, 'Mail') - }, - maxConnections: Number(configItems.smtpMaxConnections), - maxMessages: Number(configItems.smtpMaxMessages), - tls: { - rejectUnauthorized: !configItems.smtpSelfSigned - } - }); + + let oldListeners = []; + if (module.exports.transport) { + oldListeners = module.exports.transport.listeners('idle'); + module.exports.transport.removeAllListeners('idle'); + module.exports.transport.removeAllListeners('stream'); + module.exports.transport.checkThrottling = null; + } + + let sendingRate = Number(configItems.smtpThrottling) || 0; + if (sendingRate) { + // convert to messages/second + sendingRate = sendingRate / 3600; + } + + let transportOptions; + + let logfunc = function () { + let args = [].slice.call(arguments); + let level = args.shift(); + args.shift(); + args.unshift('Mail'); + log[level](...args); + }; + + if (configItems.mailTransport === 'smtp' || !configItems.mailTransport) { + transportOptions = { + pool: true, + host: configItems.smtpHostname, + port: Number(configItems.smtpPort) || false, + secure: configItems.smtpEncryption === 'TLS', + ignoreTLS: configItems.smtpEncryption === 'NONE', + auth: configItems.smtpDisableAuth ? false : { + user: configItems.smtpUser, + pass: configItems.smtpPass + }, + debug: !!configItems.smtpLog, + logger: !configItems.smtpLog ? false : { + debug: logfunc.bind(null, 'verbose'), + info: logfunc.bind(null, 'info'), + error: logfunc.bind(null, 'error') + }, + maxConnections: Number(configItems.smtpMaxConnections), + maxMessages: Number(configItems.smtpMaxMessages), + tls: { + rejectUnauthorized: !configItems.smtpSelfSigned + } + }; + } else if (configItems.mailTransport === 'ses') { + transportOptions = { + SES: new aws.SES({ + apiVersion: '2010-12-01', + accessKeyId: configItems.sesKey, + secretAccessKey: configItems.sesSecret, + region: configItems.sesRegion + }), + debug: !!configItems.smtpLog, + logger: !configItems.smtpLog ? false : { + debug: logfunc.bind(null, 'verbose'), + info: logfunc.bind(null, 'info'), + error: logfunc.bind(null, 'error') + }, + maxConnections: Number(configItems.smtpMaxConnections), + sendingRate, + tls: { + rejectUnauthorized: !configItems.smtpSelfSigned + } + }; + } else { + return callback(new Error(_('Invalid mail transport'))); + } + + module.exports.transport = nodemailer.createTransport(transportOptions, config.nodemailer); + module.exports.transport.use('stream', openpgpEncrypt({ signingKey: configItems.pgpPrivateKey, passphrase: configItems.pgpPassphrase })); - return callback(null, module.exports.transport); + if (oldListeners.length) { + log.info('Mail', 'Reattaching %s idle listeners', oldListeners.length); + oldListeners.forEach(listener => module.exports.transport.on('idle', listener)); + } + + + if (configItems.mailTransport === 'smtp' || !configItems.mailTransport) { + + let throttling = Number(configItems.smtpThrottling) || 0; + if (throttling) { + throttling = 1 / (throttling / (3600 * 1000)); + } + + let lastCheck = Date.now(); + + module.exports.transport.checkThrottling = function (next) { + if (!throttling) { + return next(); + } + let nextCheck = Date.now(); + let checkDiff = (nextCheck - lastCheck); + if (checkDiff < throttling) { + log.verbose('Mail', 'Throttling next message in %s sec.', (throttling - checkDiff) / 1000); + setTimeout(() => { + lastCheck = Date.now(); + next(); + }, throttling - checkDiff); + } else { + lastCheck = nextCheck; + next(); + } + }; + } else { + module.exports.transport.checkThrottling = next => next(); + } + + db.clearCache('sender', () => { + callback(null, module.exports.transport); + }); }); } diff --git a/lib/models/blacklist.js b/lib/models/blacklist.js new file mode 100644 index 00000000..5aae8ccc --- /dev/null +++ b/lib/models/blacklist.js @@ -0,0 +1,86 @@ +'use strict'; + +let db = require('../db'); + +module.exports.get = (start, limit, search, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + search = '%' + search + '%'; + connection.query('SELECT SQL_CALC_FOUND_ROWS `email` FROM blacklist WHERE `email` LIKE ? ORDER BY `email` LIMIT ? OFFSET ?', [search, limit, start], (err, rows) => { + if (err) { + return callback(err); + } + + connection.query('SELECT FOUND_ROWS() AS total', (err, total) => { + connection.release(); + if (err) { + return callback(err); + } + let emails = []; + rows.forEach(email => { + emails.push(email.email); + }); + return callback(null, emails, total && total[0] && total[0].total); + }); + }); + }); +}; + +module.exports.add = (email, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('INSERT IGNORE INTO `blacklist` (`email`) VALUES(?)', email, err => { + if (err) { + return callback(err); + } + + connection.release(); + return callback(null, null); + + }); + }); +}; + +module.exports.delete = (email, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('DELETE FROM `blacklist` WHERE `email`=?', email, err => { + if (err) { + return callback(err); + } + + connection.release(); + return callback(null, null); + + }); + }); +}; + +module.exports.isblacklisted = (email, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT `email` FROM blacklist WHERE `email`=?', email, (err, rows) => { + if (err) { + return callback(err); + } + + connection.release(); + if (rows.length > 0) { + return callback(null, true); + } else { + return callback(null, false); + } + }); + }); +}; diff --git a/lib/models/campaigns.js b/lib/models/campaigns.js index 672541df..2db3a8d8 100644 --- a/lib/models/campaigns.js +++ b/lib/models/campaigns.js @@ -7,35 +7,102 @@ let templates = require('./templates'); let segments = require('./segments'); let subscriptions = require('./subscriptions'); let shortid = require('shortid'); +let isUrl = require('is-url'); +let feed = require('../feed'); +let log = require('npmlog'); +let mailer = require('../mailer'); +let humanize = require('humanize'); +let _ = require('../translate')._; +let util = require('util'); +let tableHelpers = require('../table-helpers'); -let allowedKeys = ['description', 'from', 'address', 'subject', 'template', 'template_url', 'list', 'segment', 'html', 'text']; +let allowedKeys = ['description', 'from', 'address', 'reply_to', 'subject', 'editor_name', 'editor_data', 'template', 'source_url', 'list', 'segment', 'html', 'text', 'click_tracking_disabled', 'open_tracking_disabled', 'unsubscribe']; module.exports.list = (start, limit, callback) => { + tableHelpers.list('campaigns', ['*'], 'scheduled', null, start, limit, callback); +}; + +module.exports.filter = (request, parent, callback) => { + let queryData; + if (parent) { + queryData = { + // only find normal and RSS parent campaigns at this point + where: '`parent`=?', + values: [parent] + }; + } else { + queryData = { + // only find normal and RSS parent campaigns at this point + where: '`type` IN (?,?,?)', + values: [1, 2, 4] + }; + } + + tableHelpers.filter('campaigns', ['*'], request, ['#', 'name', 'description', 'status', 'created'], ['name'], 'created DESC', queryData, callback); +}; + +module.exports.filterQuicklist = (request, callback) => { + tableHelpers.filter('campaigns', ['id', 'name', 'description', 'created'], request, ['#', 'name', 'description', 'created'], ['name'], 'name ASC', null, callback); +}; + +module.exports.filterClickedSubscribers = (campaign, linkId, request, columns, callback) => { + let queryData = { + where: 'campaign_tracker__' + campaign.id + '.list=? AND campaign_tracker__' + campaign.id + '.link=?', + values: [campaign.list, linkId] + }; + + tableHelpers.filter('subscription__' + campaign.list + ' JOIN campaign_tracker__' + campaign.id + ' ON campaign_tracker__' + campaign.id + '.subscriber=subscription__' + campaign.list + '.id', ['*'], request, columns, ['email', 'first_name', 'last_name'], 'email ASC', queryData, callback); +}; + +module.exports.statsClickedSubscribersByColumn = (campaign, linkId, request, column, limit, callback) => { db.getConnection((err, connection) => { if (err) { return callback(err); } - connection.query('SELECT SQL_CALC_FOUND_ROWS * FROM campaigns ORDER BY name LIMIT ? OFFSET ?', [limit, start], (err, rows) => { + let query_template = 'SELECT ?? AS data, COUNT(*) AS cnt FROM `subscription__%d` JOIN `campaign_tracker__%d` ON `campaign_tracker__%d`.`list`=%d AND `campaign_tracker__%d`.`subscriber`=`subscription__%d`.`id` AND `campaign_tracker__%d`.`link`=%d GROUP BY ?? ORDER BY COUNT(??) DESC, ??'; + let query = util.format(query_template, campaign.list, campaign.id, campaign.id, campaign.list, campaign.id, campaign.list, campaign.id, linkId); + + connection.query(query, [column, column, column, column], (err, rows) => { + connection.release(); if (err) { - connection.release(); return callback(err); } - connection.query('SELECT FOUND_ROWS() AS total', (err, total) => { - connection.release(); - if (err) { - return callback(err); + + let data = {}; + let dataList = []; + let total = 0; + + rows.forEach((row, index) => { + if (index < limit) { + data[row.data] = row.cnt; + } else { + data.other = (data.other ? data.other : 0) + row.cnt; } - return callback(null, rows, total && total[0] && total[0].total); + total += row.cnt; }); + Object.keys(data).forEach(key => { + let name = key + ': ' + data[key]; + dataList.push([name, data[key]]); + }); + return callback(null, dataList, total); }); }); }; +module.exports.filterStatusSubscribers = (campaign, status, request, columns, callback) => { + let queryData = { + where: 'campaign__' + campaign.id + '.list=? AND campaign__' + campaign.id + '.segment=? AND campaign__' + campaign.id + '.status=?', + values: [campaign.list, campaign.segment && campaign.segment.id || 0, status] + }; + + tableHelpers.filter('subscription__' + campaign.list + ' JOIN campaign__' + campaign.id + ' ON campaign__' + campaign.id + '.subscription=subscription__' + campaign.list + '.id', ['*'], request, columns, ['email', 'first_name', 'last_name'], 'email ASC', queryData, callback); +}; + module.exports.getByCid = (cid, callback) => { cid = (cid || '').toString().trim(); if (!cid) { - return callback(new Error('Missing Campaign ID')); + return callback(new Error(_('Missing Campaign ID'))); } db.getConnection((err, connection) => { if (err) { @@ -62,7 +129,7 @@ module.exports.get = (id, withSegment, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Campaign ID')); + return callback(new Error(_('Missing Campaign ID'))); } db.getConnection((err, connection) => { @@ -82,34 +149,243 @@ module.exports.get = (id, withSegment, callback) => { let campaign = tools.convertKeys(rows[0]); - if (!campaign.segment || !withSegment) { - return callback(null, campaign); - } else { - segments.get(campaign.segment, (err, segment) => { - if (err || !segment) { - // ignore - return callback(null, campaign); - } - segments.subscribers(segment.id, true, (err, subscribers) => { - if (err || !subscribers) { - segment.subscribers = 0; - } else { - segment.subscribers = subscribers; + let handleSegment = () => { + + if (!campaign.segment || !withSegment) { + return callback(null, campaign); + } else { + segments.get(campaign.segment, (err, segment) => { + if (err || !segment) { + // ignore + return callback(null, campaign); } - campaign.segment = segment; - return callback(null, campaign); + segments.subscribers(segment.id, true, (err, subscribers) => { + if (err || !subscribers) { + segment.subscribers = 0; + } else { + segment.subscribers = subscribers; + } + campaign.segment = segment; + return callback(null, campaign); + }); }); - }); + } + }; + + if (!campaign.parent) { + return handleSegment(); } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + connection.query('SELECT `id`, `cid`, `name` FROM campaigns WHERE id=?', [campaign.parent], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return handleSegment(); + } + + campaign.parent = tools.convertKeys(rows[0]); + return handleSegment(); + }); + }); }); }); }; -module.exports.create = (campaign, callback) => { +module.exports.getAttachments = (campaign, callback) => { + campaign = Number(campaign) || 0; + + if (campaign < 1) { + return callback(new Error(_('Missing Campaign ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let keys = ['id', 'filename', 'content_type', 'size', 'created']; + + connection.query('SELECT `' + keys.join('`, `') + '` FROM `attachments` WHERE `campaign`=?', [campaign], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, []); + } + + let attachments = rows.map((row, i) => { + row = tools.convertKeys(row); + row.index = i + 1; + row.size = humanize.filesize(Number(row.size) || 0); + return row; + }); + return callback(null, attachments); + }); + }); +}; + +module.exports.addAttachment = (id, attachment, callback) => { + + let size = attachment.content ? attachment.content.length : 0; + + if (!size) { + return callback(new Error(_('Emtpy or too large attahcment'))); + } + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let keys = ['campaign', 'size']; + let values = [id, size]; + + Object.keys(attachment).forEach(key => { + let value; + if (Buffer.isBuffer(attachment[key])) { + value = attachment[key]; + } else { + value = typeof attachment[key] === 'number' ? attachment[key] : (attachment[key] || '').toString().trim(); + } + + key = tools.toDbKey(key); + keys.push(key); + values.push(value); + }); + + let query = 'INSERT INTO `attachments` (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')'; + connection.query(query, values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + let attachmentId = result && result.insertId || false; + return callback(null, attachmentId); + }); + }); +}; + +module.exports.deleteAttachment = (id, attachment, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'DELETE FROM `attachments` WHERE `id`=? AND `campaign`=? LIMIT 1'; + connection.query(query, [attachment, id], (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + let deleted = result && result.affectedRows || false; + return callback(null, deleted); + }); + }); +}; + +module.exports.getAttachment = (campaign, attachment, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'SELECT * FROM `attachments` WHERE `id`=? AND `campaign`=? LIMIT 1'; + connection.query(query, [attachment, campaign], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + if (!rows || !rows.length) { + return callback(null, false); + } + + let attachment = tools.convertKeys(rows[0]); + return callback(null, attachment); + }); + }); +}; + +module.exports.getLinks = (id, linkId, callback) => { + if (!callback && typeof linkId === 'function') { + callback = linkId; + linkId = false; + } + + id = Number(id) || 0; + linkId = Number(linkId) || 0; + + if (id < 1) { + return callback(new Error(_('Missing Campaign ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query; + let values; + + if (!linkId) { + query = 'SELECT `id`, `url`, `clicks` FROM links WHERE `campaign`=? LIMIT 1000'; + values = [id]; + } else { + query = 'SELECT `id`, `url`, `clicks` FROM links WHERE `id`=? AND `campaign`=? LIMIT 1'; + values = [linkId, id]; + } + + connection.query(query, values, (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, []); + } + + let links = rows.map( + row => tools.convertKeys(row) + ).sort((a, b) => ( + a.url.replace(/^https?:\/\/(www.)?/, '').toLowerCase()).localeCompare(b.url.replace(/^https?:\/\/(www.)?/, '').toLowerCase())); + + return callback(null, links); + }); + + }); +}; + +module.exports.duplicate = (id, callback) => module.exports.get(id, true, (err, campaign) => { + if (err) { + return callback(err); + } + if (!campaign) { + return callback(new Error(_('Campaign does not exist'))); + } + campaign.name = campaign.name + ' Copy'; + return module.exports.create(campaign, false, callback); +}); + +module.exports.create = (campaign, opts, callback) => { campaign = tools.convertKeys(campaign); let name = (campaign.name || '').toString().trim(); + campaign.openTrackingDisabled = campaign.openTrackingDisabled ? 1 : 0; + campaign.clickTrackingDisabled = campaign.clickTrackingDisabled ? 1 : 0; + + opts = opts || {}; + if (/^\d+:\d+$/.test(campaign.list)) { campaign.segment = Number(campaign.list.split(':').pop()); campaign.list = Number(campaign.list.split(':').shift()); @@ -118,27 +394,80 @@ module.exports.create = (campaign, callback) => { campaign.segment = 0; } + switch ((campaign.type || '').toString().trim().toLowerCase()) { + case 'triggered': + campaign.type = 4; + break; + case 'rss': + campaign.type = 2; + break; + case 'entry': + if (opts.parent) { + campaign.type = 3; + } else { + campaign.type = 1; + } + break; + case 'normal': + default: + campaign.type = 1; + } + campaign.template = Number(campaign.template) || 0; if (!name) { - return callback(new Error('Campaign Name must be set')); + return callback(new Error(_('Campaign Name must be set'))); } - lists.get(campaign.list, (err, list) => { + if (campaign.type === 2 && (!campaign.sourceUrl || !isUrl(campaign.sourceUrl))) { + return callback(new Error(_('RSS URL must be set and needs to be a valid URL'))); + } + + let getList = (listId, callback) => { + if (campaign.type === 4) { + return callback(null, false); + } + + lists.get(listId, (err, list) => { + if (err) { + return callback(err); + } + return callback(null, list || { + id: listId + }); + }); + }; + + getList(campaign.list, err => { if (err) { return callback(err); } - if (!list) { - return callback(new Error('Selected list not found')); + + let keys = ['name', 'type']; + let values = [name, campaign.type]; + + if (campaign.type === 2) { + keys.push('status'); + values.push(5); // inactive } - let keys = ['name']; - let values = [name]; + if (campaign.type === 3) { + keys.push('status', 'parent'); + values.push(2, opts.parent); + } - let create = () => { + if (campaign.type === 4) { + keys.push('status'); + values.push(6); // active + } + + let create = next => { Object.keys(campaign).forEach(key => { let value = typeof campaign[key] === 'number' ? campaign[key] : (campaign[key] || '').toString().trim(); key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } if (allowedKeys.indexOf(key) >= 0 && keys.indexOf(key) < 0) { keys.push(key); values.push(value); @@ -149,52 +478,117 @@ module.exports.create = (campaign, callback) => { keys.push('cid'); values.push(cid); - db.getConnection((err, connection) => { + tools.prepareHtml(campaign.html, (err, preparedHtml) => { if (err) { - return callback(err); + log.error('jsdom', err); } - let query = 'INSERT INTO campaigns (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')'; - connection.query(query, values, (err, result) => { - connection.release(); + if (!preparedHtml) { + preparedHtml = campaign.html; + } + + keys.push('html_prepared'); + values.push(preparedHtml); + + db.getConnection((err, connection) => { if (err) { - return callback(err); + return next(err); } - let campaignId = result && result.insertId || false; - if (!campaignId) { - return callback(null, false); - } - - // we are going to aqcuire a lot of log info, so we are putting - // sending logs into separate tables - createCampaignTables(campaignId, err => { + let query = 'INSERT INTO campaigns (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')'; + connection.query(query, values, (err, result) => { + connection.release(); if (err) { - // FIXME: rollback - return callback(err); + return next(err); } - return callback(null, campaignId); + + let campaignId = result && result.insertId || false; + if (!campaignId) { + return next(null, false); + } + + // we are going to aqcuire a lot of log info, so we are putting + // sending logs into separate tables + createCampaignTables(campaignId, err => { + if (err) { + // FIXME: rollback + return next(err); + } + return next(null, campaignId); + }); }); }); }); }; - if (campaign.template) { + if (campaign.type === 2) { + feed.fetch(campaign.sourceUrl, (err, entries) => { + if (err) { + return callback(err); + } + + mailer.getTemplate('emails/rss-html.hbs', (err, rendererHtml) => { + if (err) { + return callback(err); + } + + campaign.html = rendererHtml(); + + create((err, campaignId) => { + if (err) { + return callback(err); + } + if (!campaignId || !entries.length) { + return callback(null, campaignId); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + // store references to already existing feed entries + // this is needed to detect new entries + let query = 'INSERT IGNORE INTO `rss` (`parent`,`guid`,`pubdate`) VALUES ' + entries.map(() => '(?,?,?)').join(','); + + values = []; + entries.forEach(entry => { + values.push(campaignId, entry.guid, entry.date); + }); + + connection.query(query, values, err => { + connection.release(); + if (err) { + // too late to report as failed + log.error('RSS', err); + } + + return callback(null, campaignId); + }); + }); + }); + }); + }); + return; + } else if (campaign.template) { templates.get(campaign.template, (err, template) => { if (err) { return callback(err); } if (!template) { - return callback(new Error('Selected template not found')); + return callback(new Error(_('Selected template not found'))); } - keys = keys.concat(['html', 'text']); - values = values.concat([template.html, template.text]); + campaign.editorName = template.editorName; + campaign.editorData = template.editorData; + campaign.html = template.html; + campaign.text = template.text; - create(); + create(callback); }); + return; } else { - create(); + return create(callback); } }); }; @@ -204,14 +598,17 @@ module.exports.update = (id, updates, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Campaign ID')); + return callback(new Error(_('Missing Campaign ID'))); } let campaign = tools.convertKeys(updates); let name = (campaign.name || '').toString().trim(); + campaign.openTrackingDisabled = campaign.openTrackingDisabled ? 1 : 0; + campaign.clickTrackingDisabled = campaign.clickTrackingDisabled ? 1 : 0; + if (!name) { - return callback(new Error('Campaign Name must be set')); + return callback(new Error(_('Campaign Name must be set'))); } if (/^\d+:\d+$/.test(campaign.list)) { @@ -222,13 +619,21 @@ module.exports.update = (id, updates, callback) => { campaign.segment = 0; } - lists.get(campaign.list, (err, list) => { + let getList = (listId, callback) => { + lists.get(listId, (err, list) => { + if (err) { + return callback(err); + } + return callback(null, list || { + id: listId + }); + }); + }; + + getList(campaign.list, err => { if (err) { return callback(err); } - if (!list) { - return callback(new Error('Selected list not found')); - } let keys = ['name']; let values = [name]; @@ -236,25 +641,87 @@ module.exports.update = (id, updates, callback) => { Object.keys(campaign).forEach(key => { let value = typeof campaign[key] === 'number' ? campaign[key] : (campaign[key] || '').toString().trim(); key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } if (allowedKeys.indexOf(key) >= 0 && keys.indexOf(key) < 0) { keys.push(key); values.push(value); } }); - db.getConnection((err, connection) => { + tools.prepareHtml(campaign.html, (err, preparedHtml) => { if (err) { - return callback(err); + log.error('jsdom', err); } - values.push(id); + if (!preparedHtml) { + preparedHtml = campaign.html; + } - connection.query('UPDATE campaigns SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => { - connection.release(); + keys.push('html_prepared'); + values.push(preparedHtml); + + db.getConnection((err, connection) => { if (err) { return callback(err); } - return callback(null, result && result.affectedRows || false); + + values.push(id); + + connection.query('UPDATE campaigns SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => { + if (err) { + connection.release(); + return callback(err); + } + let affected = result && result.affectedRows || false; + + if (!affected) { + connection.release(); + return callback(null, affected); + } + + connection.query('SELECT `type`, `source_url` FROM campaigns WHERE id=? LIMIT 1', [id], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows[0] || rows[0].type !== 2) { + // if not RSS, then nothing to do here + return callback(null, affected); + } + + // update seen rss entries to avoid sending old entries to subscribers + feed.fetch(rows[0].source_url, (err, entries) => { + if (err) { + return callback(err); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'INSERT IGNORE INTO `rss` (`parent`,`guid`,`pubdate`) VALUES ' + entries.map(() => '(?,?,?)').join(','); + + values = []; + entries.forEach(entry => { + values.push(id, entry.guid, entry.date); + }); + + connection.query(query, values, err => { + connection.release(); + if (err) { + // too late to report as failed + log.error('RSS', err); + } + return callback(null, affected); + }); + }); + }); + }); + }); }); }); }); @@ -264,7 +731,7 @@ module.exports.delete = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Campaign ID')); + return callback(new Error(_('Missing Campaign ID'))); } db.getConnection((err, connection) => { @@ -273,24 +740,19 @@ module.exports.delete = (id, callback) => { } connection.query('DELETE FROM campaigns WHERE id=? LIMIT 1', [id], (err, result) => { + connection.release(); if (err) { - connection.release(); return callback(err); } let affected = result && result.affectedRows || 0; - - connection.query('DELETE FROM links WHERE campaign=?', [id], err => { - connection.release(); + removeCampaignTables(id, err => { if (err) { return callback(err); } - removeCampaignTables(id, err => { - if (err) { - return callback(err); - } - return callback(null, affected); + db.clearCache('sender', () => { + callback(null, affected); }); }); }); @@ -351,11 +813,13 @@ module.exports.pause = (id, callback) => { // campaigns marked as status=4 are paused connection.query('UPDATE campaigns SET `status`=4, `status_change`=NOW() WHERE id=? LIMIT 1', [id], err => { + connection.release(); if (err) { - connection.release(); return callback(err); } - return callback(null, true); + db.clearCache('sender', () => { + callback(null, true); + }); }); }); }); @@ -367,7 +831,7 @@ module.exports.reset = (id, callback) => { return callback(err); } - if (campaign.status !== 3 && !(campaign.status === 2 && campaign.scheduled > new Date())) { + if (campaign.status !== 3 && campaign.status !== 4 && !(campaign.status === 2 && campaign.scheduled > new Date())) { return callback(null, false); } @@ -376,27 +840,30 @@ module.exports.reset = (id, callback) => { return callback(err); } - connection.query('UPDATE campaigns SET `status`=1, `status_change`=NULL, `delivered`=0, `opened`=0, `clicks`=0, `bounced`=0, `complained`=0, `unsubscribed`=0 WHERE id=? LIMIT 1', [id], err => { + connection.query('UPDATE campaigns SET `status`=1, `status_change`=NULL, `delivered`=0, `opened`=0, `clicks`=0, `bounced`=0, `complained`=0, `unsubscribed`=0, `blacklisted`=0 WHERE id=? LIMIT 1', [id], err => { if (err) { connection.release(); return callback(err); } - connection.query('DELETE FROM links WHERE campaign=?', [id], err => { - if (err) { - connection.release(); - return callback(err); - } - connection.query('TRUNCATE TABLE `campaign__' + id + '`', [id], err => { + + db.clearCache('sender', () => { + connection.query('UPDATE links SET `clicks`=0 WHERE campaign=?', [id], err => { if (err) { connection.release(); return callback(err); } - connection.query('TRUNCATE TABLE `campaign_tracker__' + id + '`', [id], err => { - connection.release(); + connection.query('TRUNCATE TABLE `campaign__' + id + '`', [id], err => { if (err) { + connection.release(); return callback(err); } - return callback(null, true); + connection.query('TRUNCATE TABLE `campaign_tracker__' + id + '`', [id], err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); }); }); }); @@ -405,13 +872,67 @@ module.exports.reset = (id, callback) => { }); }; +module.exports.activate = (id, callback) => { + module.exports.get(id, false, (err, campaign) => { + if (err) { + return callback(err); + } + + if (campaign.status !== 5) { + return callback(null, false); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + // campaigns marked as status=5 are paused + connection.query('UPDATE campaigns SET `status`=6, `status_change`=NOW() WHERE id=? LIMIT 1', [id], err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + }); + }); +}; + +module.exports.inactivate = (id, callback) => { + module.exports.get(id, false, (err, campaign) => { + if (err) { + return callback(err); + } + + if (campaign.status !== 6) { + return callback(null, false); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + // campaigns marked as status=6 are paused + connection.query('UPDATE campaigns SET `status`=5, `status_change`=NOW() WHERE id=? LIMIT 1', [id], err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + }); + }); +}; + module.exports.getMail = (campaignId, listId, subscriptionId, callback) => { campaignId = Number(campaignId) || 0; listId = Number(listId) || 0; subscriptionId = Number(subscriptionId) || 0; if (campaignId < 1 || listId < 1 || subscriptionId < 1) { - return callback(new Error('Invalid or missing message ID')); + return callback(new Error(_('Invalid or missing message ID'))); } db.getConnection((err, connection) => { @@ -441,7 +962,7 @@ module.exports.findMailByResponse = (responseId, callback) => { return callback(err); } - connection.query('SELECT id FROM campaigns', [], (err, campaignList) => { + connection.query('SELECT id FROM campaigns ORDER BY id DESC', [], (err, campaignList) => { if (err) { connection.release(); return callback(err); @@ -546,13 +1067,13 @@ module.exports.updateMessage = (message, status, updateSubscription, callback) = let statusCode; if (status === 'unsubscribed') { - statusCode = 2; - } - if (status === 'bounced') { - statusCode = 3; - } - if (status === 'complained') { - statusCode = 4; + statusCode = subscriptions.Status.UNSUBSCRIBED; + } else if (status === 'bounced') { + statusCode = subscriptions.Status.BOUNCED; + } else if (status === 'complained') { + statusCode = subscriptions.Status.COMPLAINED; + } else { + return callback(new Error(_('Unrecognized message status'))); } let query = 'UPDATE `campaigns` SET `' + status + '`=`' + status + '`+1 WHERE id=? LIMIT 1'; @@ -566,7 +1087,7 @@ module.exports.updateMessage = (message, status, updateSubscription, callback) = } if (updateSubscription) { - subscriptions.changeStatus(message.subscription, message.list, statusCode === 2 ? message.campaign : false, statusCode, callback); + subscriptions.changeStatus(message.list, message.subscription, statusCode === subscriptions.Status.UNSUBSCRIBED ? message.campaign : false, statusCode, callback); } else { return callback(null, true); } @@ -576,6 +1097,24 @@ module.exports.updateMessage = (message, status, updateSubscription, callback) = }); }; +module.exports.updateMessageResponse = (message, response, response_id, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'UPDATE `campaign__' + message.campaign + '` SET `response`=?, `response_id`=? WHERE id=? LIMIT 1'; + connection.query(query, [response, response_id, message.id], err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + + }); +}; + function createCampaignTables(id, callback) { let query = 'CREATE TABLE `campaign__' + id + '` LIKE campaign'; db.getConnection((err, connection) => { diff --git a/lib/models/confirmations.js b/lib/models/confirmations.js new file mode 100644 index 00000000..9cbceee6 --- /dev/null +++ b/lib/models/confirmations.js @@ -0,0 +1,91 @@ +'use strict'; + +let db = require('../db'); +let shortid = require('shortid'); +let helpers = require('../helpers'); +let _ = require('../translate')._; + +/* + Adds new entry to the confirmations tables. Generates confirmation cid, which it returns. + */ +module.exports.addConfirmation = (listId, action, ip, data, callback) => { + let cid = shortid.generate(); + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'INSERT INTO confirmations (cid, list, action, ip, data) VALUES (?,?,?,?,?)'; + connection.query(query, [cid, listId, action, ip, JSON.stringify(data || {})], (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!result || !result.affectedRows) { + return callback(new Error(_('Could not store confirmation data'))); + } + + return callback(null, cid); + }); + }); +}; + +/* + Atomically retrieves confirmation from the database, removes it from the database and returns it. + */ +module.exports.takeConfirmation = (cid, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.beginTransaction(err => { + if (err) { + connection.release(); + return callback(err); + } + + let query = 'SELECT cid, list, action, ip, data FROM confirmations WHERE cid=? LIMIT 1'; + connection.query(query, [cid], (err, rows) => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + if (!rows || !rows.length) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(null, false)); + } + + connection.query('DELETE FROM confirmations WHERE `cid`=? LIMIT 1', [cid], () => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + connection.commit(err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + connection.release(); + + let data; + try { + data = JSON.parse(rows[0].data); + } catch (E) { + data = {}; + } + + const result = { + listId: rows[0].list, + action: rows[0].action, + ip: rows[0].ip, + data + }; + + return callback(null, result); + }); + }); + }); + }); + }); +}; diff --git a/lib/models/fields.js b/lib/models/fields.js index b9076439..1b3dbe2b 100644 --- a/lib/models/fields.js +++ b/lib/models/fields.js @@ -5,31 +5,39 @@ let tools = require('../tools'); let slugify = require('slugify'); let lists = require('./lists'); let shortid = require('shortid'); +let Handlebars = require('handlebars'); +let _ = require('../translate')._; +let util = require('util'); -let allowedKeys = ['name', 'key', 'default_value', 'group', 'visible']; +let allowedKeys = ['name', 'description', 'key', 'default_value', 'group', 'group_template', 'visible']; +let allowedTypes; module.exports.grouped = ['radio', 'checkbox', 'dropdown']; module.exports.types = { - text: 'Text', - website: 'Website', - longtext: 'Multi-line text', - gpg: 'GPG Public Key', - number: 'Number', - radio: 'Radio Buttons', - checkbox: 'Checkboxes', - dropdown: 'Drop Down', - 'date-us': 'Date (MM/DD/YYY)', - 'date-eur': 'Date (DD/MM/YYYY)', - 'birthday-us': 'Birthday (MM/DD)', - 'birthday-eur': 'Birthday (DD/MM)', - option: 'Option' + text: _('Text'), + website: _('Website'), + longtext: _('Multi-line text'), + gpg: _('GPG Public Key'), + number: _('Number'), + radio: _('Radio Buttons'), + checkbox: _('Checkboxes'), + dropdown: _('Drop Down'), + 'date-us': _('Date (MM/DD/YYY)'), + 'date-eur': _('Date (DD/MM/YYYY)'), + 'birthday-us': _('Birthday (MM/DD)'), + 'birthday-eur': _('Birthday (DD/MM)'), + json: _('JSON value for custom rendering'), + option: _('Option') }; +module.exports.allowedTypes = allowedTypes = Object.keys(module.exports.types); + module.exports.genericTypes = { text: 'string', website: 'string', longtext: 'textarea', gpg: 'textarea', + json: 'textarea', number: 'number', 'date-us': 'date', 'date-eur': 'date', @@ -42,7 +50,7 @@ module.exports.list = (listId, callback) => { listId = Number(listId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } db.getConnection((err, connection) => { @@ -50,7 +58,7 @@ module.exports.list = (listId, callback) => { return callback(err); } - let query = 'SELECT * FROM custom_fields WHERE list=?'; + let query = 'SELECT * FROM custom_fields WHERE list=? ORDER BY id'; connection.query(query, [listId], (err, rows) => { connection.release(); if (err) { @@ -87,7 +95,7 @@ module.exports.get = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } db.getConnection((err, connection) => { @@ -101,7 +109,9 @@ module.exports.get = (id, callback) => { if (err) { return callback(err); } - return callback(null, rows && rows[0] && tools.convertKeys(rows[0]) || false); + let field = rows && rows[0] && tools.convertKeys(rows[0]) || false; + field.isGroup = module.exports.grouped.indexOf(field.type) >= 0 || field.type === 'json'; + return callback(null, field); }); }); }; @@ -110,20 +120,24 @@ module.exports.create = (listId, field, callback) => { listId = Number(listId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } field = tools.convertKeys(field); if (field.type === 'option' && !field.group) { - return callback(new Error('Option field requires a group to be selected')); + return callback(new Error(_('Option field requires a group to be selected'))); } if (field.type !== 'option') { field.group = null; } - addCustomField(listId, field.name, field.defaultValue, field.type, field.group, field.visible, callback); + field.description = (field.description || '').toString().trim() || null; + field.defaultValue = (field.defaultValue || '').toString().trim() || null; + field.groupTemplate = (field.groupTemplate || '').toString().trim() || null; + + addCustomField(listId, field.name, field.description, field.defaultValue, field.type, field.group, field.groupTemplate, field.visible, callback); }; module.exports.update = (id, updates, callback) => { @@ -133,17 +147,21 @@ module.exports.update = (id, updates, callback) => { updates = tools.convertKeys(updates); if (id < 1) { - return callback(new Error('Missing Field ID')); + return callback(new Error(_('Missing Field ID'))); } if (!(updates.name || '').toString().trim()) { - return callback(new Error('Field Name must be set')); + return callback(new Error(_('Field Name must be set'))); } if (updates.key) { updates.key = slugify(updates.key, '_').toUpperCase(); } + updates.description = (updates.description || '').toString().trim() || null; + updates.defaultValue = (updates.defaultValue || '').toString().trim() || null; + updates.groupTemplate = (updates.groupTemplate || '').toString().trim() || null; + updates.visible = updates.visible ? 1 : 0; let name = (updates.name || '').toString().trim(); @@ -180,7 +198,7 @@ module.exports.delete = (fieldId, callback) => { fieldId = Number(fieldId) || 0; if (fieldId < 1) { - return callback(new Error('Missing Field ID')); + return callback(new Error(_('Missing Field ID'))); } db.getConnection((err, connection) => { @@ -197,7 +215,7 @@ module.exports.delete = (fieldId, callback) => { if (!rows || !rows.length) { connection.release(); - return callback(new Error('Custom field not found')); + return callback(new Error(_('Custom field not found'))); } let field = tools.convertKeys(rows[0]); @@ -261,25 +279,24 @@ module.exports.delete = (fieldId, callback) => { }); }; -function addCustomField(listId, name, defaultValue, type, group, visible, callback) { +function addCustomField(listId, name, description, defaultValue, type, group, groupTemplate, visible, callback) { type = (type || '').toString().trim().toLowerCase(); group = Number(group) || null; listId = Number(listId) || 0; let column = null; let key = slugify('merge ' + name, '_').toUpperCase(); - let allowedTypes = ['text', 'number', 'radio', 'checkbox', 'dropdown', 'date-us', 'date-eur', 'birthday-us', 'birthday-eur', 'website', 'option', 'gpg', 'longtext']; if (allowedTypes.indexOf(type) < 0) { - return callback(new Error('Unknown column type ' + type)); + return callback(new Error(util.format(_('Unknown column type %s'), type))); } if (!name) { - return callback(new Error('Missing column name')); + return callback(new Error(_('Missing column name'))); } if (listId <= 0) { - return callback(new Error('Missing list ID')); + return callback(new Error(_('Missing list ID'))); } lists.get(listId, (err, list) => { @@ -287,7 +304,7 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba return callback(err); } if (!list) { - return callback('Provided List ID not found'); + return callback(_('Provided List ID not found')); } db.getConnection((err, connection) => { @@ -296,11 +313,11 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba } if (module.exports.grouped.indexOf(type) < 0) { - column = ('custom_' + slugify(name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9\_]/g, ''); + column = ('custom_' + slugify(name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, ''); } - let query = 'INSERT INTO custom_fields (`list`, `name`, `key`,`default_value`, `type`, `group`, `column`, `visible`) VALUES(?,?,?,?,?,?,?,?)'; - connection.query(query, [listId, name, key, defaultValue, type, group, column, visible ? 1 : 0], (err, result) => { + let query = 'INSERT INTO custom_fields (`list`, `name`, `description`, `key`,`default_value`, `type`, `group`, `group_template`, `column`, `visible`) VALUES(?,?,?,?,?,?,?,?,?,?)'; + connection.query(query, [listId, name, description, key, defaultValue, type, group, groupTemplate, column, visible ? 1 : 0], (err, result) => { if (err) { connection.release(); @@ -308,31 +325,37 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba } let fieldId = result && result.insertId; + let indexQuery; switch (type) { case 'text': case 'website': - query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` VARCHAR(255) DEFAULT NULL, ADD INDEX (' + column + ')'; + query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` VARCHAR(255) DEFAULT NULL'; + indexQuery = 'CREATE INDEX ' + column + '_index ON `subscription__' + listId + '` (`column`);'; break; case 'gpg': case 'longtext': + case 'json': query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` TEXT DEFAULT NULL'; break; case 'number': - query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` INT(11) DEFAULT NULL, ADD INDEX (' + column + ')'; + query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` INT(11) DEFAULT NULL'; + indexQuery = 'CREATE INDEX ' + column + '_index ON `subscription__' + listId + '` (`column`);'; break; case 'option': - query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` TINYINT(4) UNSIGNED NOT NULL DEFAULT \'0\', ADD INDEX (' + column + ')'; + query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` TINYINT(4) UNSIGNED NOT NULL DEFAULT \'0\''; + indexQuery = 'CREATE INDEX ' + column + '_index ON `subscription__' + listId + '` (`column`);'; break; case 'date-us': case 'date-eur': case 'birthday-us': case 'birthday-eur': - query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` TIMESTAMP NULL DEFAULT NULL, ADD INDEX (' + column + ')'; + query = 'ALTER TABLE `subscription__' + listId + '` ADD COLUMN `' + column + '` DATETIME NULL DEFAULT NULL'; + indexQuery = 'CREATE INDEX ' + column + '_index ON `subscription__' + listId + '` (`column`);'; break; default: connection.release(); - return callback(null, true); + return callback(null, fieldId, key); } connection.query(query, err => { @@ -340,15 +363,25 @@ function addCustomField(listId, name, defaultValue, type, group, visible, callba connection.query('DELETE FROM custom_fields WHERE id=? LIMIT 1', [fieldId], () => connection.release()); return callback(err); } - connection.release(); - return callback(null, fieldId); + if (!indexQuery) { + connection.release(); + return callback(null, fieldId, key); + } else { + connection.query(query, err => { + if (err) { + // ignore index errors + } + connection.release(); + return callback(null, fieldId, key); + }); + } }); }); }); }); } -module.exports.getRow = (fieldList, values, useDate, showAll) => { +module.exports.getRow = (fieldList, values, useDate, showAll, onlyExisting) => { let valueList = {}; let row = []; @@ -363,6 +396,11 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { }); fieldList.filter(field => showAll || field.visible).forEach(field => { + if (onlyExisting && field.column && !valueList.hasOwnProperty(field.column)) { + // ignore missing values + return; + } + /* eslint-disable indent */ switch (field.type) { case 'text': case 'website': @@ -370,14 +408,47 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { case 'longtext': { let item = { + id: field.id, type: field.type, name: field.name, + description: field.description, column: field.column, value: (valueList[field.column] || '').toString().trim(), visible: !!field.visible, mergeTag: field.key, mergeValue: (valueList[field.column] || '').toString().trim() || field.defaultValue, - ['type' + (field.type || '').toString().trim().replace(/(?:^|\-)([a-z])/g, (m, c) => c.toUpperCase())]: true + ['type' + (field.type || '').toString().trim().replace(/(?:^|-)([a-z])/g, (m, c) => c.toUpperCase())]: true + }; + row.push(item); + break; + } + case 'json': + { + let value; + let json = (valueList[field.column] || '').toString().trim(); + try { + let parsed = JSON.parse(json); + if (Array.isArray(parsed)) { + parsed = { + values: parsed + }; + } + value = json ? render(field.groupTemplate, parsed) : ''; + } catch (E) { + value = E.message; + } + + let item = { + id: field.id, + type: field.type, + name: field.name, + description: field.description, + column: field.column, + value: (valueList[field.column] || '').toString().trim(), + visible: !!field.visible, + mergeTag: field.key, + mergeValue: value || field.defaultValue, + ['type' + (field.type || '').toString().trim().replace(/(?:^|-)([a-z])/g, (m, c) => c.toUpperCase())]: true }; row.push(item); break; @@ -385,14 +456,16 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { case 'number': { let item = { + id: field.id, type: field.type, name: field.name, + description: field.description, column: field.column, value: Number(valueList[field.column]) || 0, visible: !!field.visible, mergeTag: field.key, - mergeValue: Number(valueList[field.column]) || field.defaultValue || 0, - ['type' + (field.type || '').toString().trim().replace(/(?:^|\-)([a-z])/g, (m, c) => c.toUpperCase())]: true + mergeValue: (Number(valueList[field.column]) || Number(field.defaultValue) || 0).toString(), + ['type' + (field.type || '').toString().trim().replace(/(?:^|-)([a-z])/g, (m, c) => c.toUpperCase())]: true }; row.push(item); break; @@ -401,25 +474,46 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { case 'radio': case 'checkbox': { + let hasSelectedOption = (field.options || []).some(subField => subField.column && valueList[subField.column]); let item = { + id: field.id, type: field.type, name: field.name, + description: field.description, visible: !!field.visible, key: 'group-g' + field.id, mergeTag: field.key, mergeValue: field.defaultValue, - ['type' + (field.type || '').toString().trim().replace(/(?:^|\-)([a-z])/g, (m, c) => c.toUpperCase())]: true, - options: (field.options || []).map(subField => ({ - type: subField.type, - name: subField.name, - column: subField.column, - value: valueList[subField.column] ? 1 : 0, - visible: !!subField.visible, - mergeTag: subField.key, - mergeValue: valueList[subField.column] ? subField.name : subField.defaultValue - })) + ['type' + (field.type || '').toString().trim().replace(/(?:^|-)([a-z])/g, (m, c) => c.toUpperCase())]: true, + groupTemplate: field.groupTemplate, + options: (field.options || []).map(subField => { + if (onlyExisting && subField.column && !valueList.hasOwnProperty(subField.column)) { + if (hasSelectedOption && field.type !== 'checkbox') { + // Set all radio or dropdown options if a selection for the group is present + } else if (field.type === 'checkbox' && values['originGroupG' + field.id] === 'webform') { + // Set all checkbox options if origin is webform (subscribe, manage, or admin edit) #333 + // Atomic updates via API call or CSV import still possible + } else { + // ignore missing values + return false; + } + } + return { + type: subField.type, + name: subField.name, + description: subField.description, + column: subField.column, + value: valueList[subField.column] ? 1 : 0, + visible: !!subField.visible, + mergeTag: subField.key, + mergeValue: valueList[subField.column] ? subField.name : subField.defaultValue + }; + }).filter(subField => subField) }; - item.value = item.options.filter(subField => showAll || subField.visible && subField.value).map(subField => subField.name).join(', '); + let subItems = item.options.filter(subField => (showAll || subField.visible) && subField.value).map(subField => subField.name); + item.value = field.groupTemplate ? render(field.groupTemplate, { + values: subItems + }) : subItems.join(', '); item.mergeValue = item.value || field.defaultValue; row.push(item); break; @@ -429,7 +523,7 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { case 'date-us': case 'birthday-us': { - let isUs = /\-us$/.test(field.type); + let isUs = /-us$/.test(field.type); let isYear = field.type.indexOf('date-') === 0; let value = valueList[field.column]; let day, month, year; @@ -442,18 +536,27 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { } else { value = (value || '').toString().trim(); - let parts = value.match(/(\d+)\D+(\d+)(?:\D+(\d+))?/); - if (!parts) { - value = null; + // try international format first YYYY-MM-DD + let parts = value.match(/(\d{4})\D+(\d{2})(?:\D+(\d{2})\b)?/); + if (parts) { + year = Number(parts[1]) || 2000; + month = Number(parts[2]) || 0; + day = Number(parts[3]) || 0; + value = new Date(Date.UTC(year, month - 1, day)); } else { - day = Number(parts[isUs ? 2 : 1]) || 0; - month = Number(parts[isUs ? 1 : 2]) || 0; - year = Number(parts[3]) || 2000; - - if (!day || !month) { + parts = value.match(/(\d+)\D+(\d+)(?:\D+(\d+)\b)?/); + if (!parts) { value = null; } else { - value = new Date(Date.UTC(year, month - 1, day)); + day = Number(parts[isUs ? 2 : 1]) || 0; + month = Number(parts[isUs ? 1 : 2]) || 0; + year = Number(parts[3]) || 2000; + + if (!day || !month) { + value = null; + } else { + value = new Date(Date.UTC(year, month - 1, day)); + } } } } @@ -473,19 +576,22 @@ module.exports.getRow = (fieldList, values, useDate, showAll) => { } let item = { + id: field.id, type: field.type, name: field.name, + description: field.description, column: field.column, value: useDate ? value : formatted, visible: !!field.visible, mergeTag: field.key, mergeValue: (useDate ? value : formatted) || field.defaultValue, - ['type' + (field.type || '').toString().trim().replace(/(?:^|\-)([a-z])/g, (m, c) => c.toUpperCase())]: true + ['type' + (field.type || '').toString().trim().replace(/(?:^|-)([a-z])/g, (m, c) => c.toUpperCase())]: true }; row.push(item); break; } } + /* eslint-enable indent */ }); return row; @@ -511,3 +617,9 @@ module.exports.getValues = (row, showAll) => { return result; }; + + +function render(template, options) { + let renderer = Handlebars.compile(template); + return renderer(options); +} diff --git a/lib/models/forms.js b/lib/models/forms.js new file mode 100644 index 00000000..9550077b --- /dev/null +++ b/lib/models/forms.js @@ -0,0 +1,418 @@ +'use strict'; + +let db = require('../db'); +let fs = require('fs'); +let path = require('path'); +let tools = require('../tools'); +let mjml = require('mjml'); +let _ = require('../translate')._; + +let allowedKeys = [ + 'name', + 'description', + 'fields_shown_on_subscribe', + 'fields_shown_on_manage', + 'layout', + 'form_input_style', + 'web_subscribe', + 'web_confirm_subscription_notice', + 'mail_confirm_subscription_html', + 'mail_confirm_subscription_text', + 'mail_already_subscribed_html', + 'mail_already_subscribed_text', + 'web_subscribed_notice', + 'mail_subscription_confirmed_html', + 'mail_subscription_confirmed_text', + 'web_manage', + 'web_manage_address', + 'web_updated_notice', + 'web_unsubscribe', + 'web_confirm_unsubscription_notice', + 'mail_confirm_unsubscription_html', + 'mail_confirm_unsubscription_text', + 'mail_confirm_address_change_html', + 'mail_confirm_address_change_text', + 'web_unsubscribed_notice', + 'mail_unsubscription_confirmed_html', + 'mail_unsubscription_confirmed_text', + 'web_manual_unsubscribe_notice' +]; + + +module.exports.list = (listId, callback) => { + listId = Number(listId) || 0; + + if (listId < 1) { + return callback(new Error(_('Missing List ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM custom_forms WHERE list=? ORDER BY id', [listId], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + let formList = rows && rows.map(row => tools.convertKeys(row)) || []; + return callback(null, formList); + }); + }); +}; + +module.exports.get = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing Form ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM custom_forms WHERE id=? LIMIT 1', [id], (err, rows) => { + if (err) { + connection.release(); + return callback(err); + } + + let form = rows && rows[0] && tools.convertKeys(rows[0]) || false; + + if (!form) { + connection.release(); + return callback(new Error('Selected form not found')); + } + + connection.query('SELECT * FROM custom_forms_data WHERE form=?', [id], (err, data_rows = []) => { + connection.release(); + if (err) { + return callback(err); + } + + data_rows.forEach(data_row => { + let modelKey = tools.fromDbKey(data_row.data_key); + form[modelKey] = data_row.data_value; + }); + + return callback(null, form); + }); + }); + }); +}; + + +module.exports.create = (listId, form, callback) => { + listId = Number(listId) || 0; + + if (listId < 1) { + return callback(new Error(_('Missing Form ID'))); + } + + form = tools.convertKeys(form); + form = setDefaultValues(form); + form.name = (form.name || '').toString().trim(); + + if (!form.name) { + return callback(new Error(_('Form Name must be set'))); + } + + let keys = ['list']; + let values = [listId]; + + Object.keys(form).forEach(key => { + let value = form[key].trim(); + key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } + if (allowedKeys.indexOf(key) >= 0) { + keys.push(key); + values.push(value); + } + }); + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let filtered = filterKeysAndValues(keys, values, 'exclude', ['mail_', 'web_']); + let query = 'INSERT INTO custom_forms (' + filtered.keys.join(', ') + ') VALUES (' + filtered.values.map(() => '?').join(',') + ')'; + + connection.query(query, filtered.values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + let formId = result && result.insertId; + + if (!formId) { + return callback(new Error('Invalid custom_forms insertId')); + } + + let jobs = 1; + let error = null; + + let done = err => { + jobs--; + error = err ? err : error; // One's enough + jobs === 0 && callback(error, formId); + }; + + filtered = filterKeysAndValues(keys, values, 'include', ['mail_', 'web_']); + + filtered.keys.forEach((key, index) => { + jobs++; + + db.getConnection((err, connection) => { + if (err) { + return done(err); + } + + connection.query('INSERT INTO custom_forms_data (form, data_key, data_value) VALUES (?, ?, ?)', [formId, key, filtered.values[index]], err => { + connection.release(); + if (err) { + return done(err); + } + + return done(null); + }); + }); + }); + + done(null); + }); + }); +}; + +module.exports.update = (id, updates, callback) => { + updates = updates || {}; + id = Number(id) || 0; + + updates = tools.convertKeys(updates); + + if (id < 1) { + return callback(new Error(_('Missing Form ID'))); + } + + if (!(updates.name || '').toString().trim()) { + return callback(new Error(_('Form Name must be set'))); + } + + let keys = []; + let values = []; + + Object.keys(updates).forEach(key => { + let value = typeof updates[key] === 'string' ? updates[key].trim() : updates[key]; + key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } + if (allowedKeys.indexOf(key) >= 0) { + keys.push(key); + values.push(value); + } + }); + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let filtered = filterKeysAndValues(keys, values, 'exclude', ['mail_', 'web_']); + let query = 'UPDATE custom_forms SET ' + filtered.keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1'; + + connection.query(query, filtered.values.concat(id), (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + let affectedRows = result && result.affectedRows; + + let jobs = 1; + let error = null; + + let done = err => { + jobs--; + error = err ? err : error; // One's enough + + if (jobs === 0) { + if (error) { + return callback(error); + } + + // Save then validate, as otherwise their work get's lost ... + err = testForMjmlErrors(keys, values); + if (err) { + return callback(err); + } + + return callback(null, affectedRows); + } + }; + + filtered = filterKeysAndValues(keys, values, 'include', ['mail_', 'web_']); + + filtered.keys.forEach((key, index) => { + jobs++; + + db.getConnection((err, connection) => { + if (err) { + return done(err); + } + + connection.query('UPDATE custom_forms_data SET data_value=? WHERE data_key=? AND form=?', [filtered.values[index], key, id], err => { + connection.release(); + if (err) { + return done(err); + } + + return done(null); + }); + }); + }); + + done(null); + }); + }); +}; + +module.exports.delete = (formId, callback) => { + formId = Number(formId) || 0; + + if (formId < 1) { + return callback(new Error(_('Missing Form ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM custom_forms WHERE id=? LIMIT 1', [formId], (err, rows) => { + if (err) { + connection.release(); + return callback(err); + } + + if (!rows || !rows.length) { + connection.release(); + return callback(new Error(_('Custom form not found'))); + } + + connection.query('DELETE FROM custom_forms WHERE id=? LIMIT 1', [formId], err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + }); + }); +}; + +function setDefaultValues(form) { + let getContents = fileName => { + try { + let basePath = path.join(__dirname, '..', '..'); + let template = fs.readFileSync(path.join(basePath, fileName), 'utf8'); + return template.replace(/\{\{#translate\}\}(.*?)\{\{\/translate\}\}/g, (m, s) => _(s)); + } catch (err) { + return false; + } + }; + + allowedKeys.forEach(key => { + let modelKey = tools.fromDbKey(key); + let base = 'views/subscription/' + key.replace(/_/g, '-'); + + if (key.startsWith('mail') || key.startsWith('web')) { + form[modelKey] = getContents(base + '.mjml.hbs') || getContents(base + '.hbs') || ''; + } + }); + + form.layout = getContents('views/subscription/layout.mjml.hbs') || ''; + form.formInputStyle = getContents('public/subscription/form-input-style.css') || '@import url(/subscription/form-input-style.css);'; + + return form; +} + +function filterKeysAndValues(keysIn, valuesIn, method = 'include', prefixes = []) { + let values = []; + + let prefixMatch = key => ( + prefixes.some(prefix => key.startsWith(prefix)) + ); + + let keys = keysIn.filter((key, index) => { + if ((method === 'include' && prefixMatch(key)) || (method === 'exclude' && !prefixMatch(key))) { + values.push(valuesIn[index]); + return true; + } + return false; + }); + + return { + keys, + values + }; +} + +function testForMjmlErrors(keys, values) { + + let errors = []; + let testLayout = '{{{body}}}'; + + let hasMjmlError = (template, layout = testLayout) => { + let source = layout.replace(/\{\{\{body\}\}\}/g, template); + let compiled; + + try { + compiled = mjml.mjml2html(source); + } catch (err) { + return err; + } + + if (compiled.errors.length) { + return compiled.errors[0].message || compiled.errors[0]; + } + + return null; + }; + + keys.forEach((key, index) => { + if (key.startsWith('mail_') || key.startsWith('web_')) { + + let template = values[index]; + let err = hasMjmlError(template); + + err && errors.push(key + ': ' + (err.message || err)); + key === 'mail_confirm_html' && !template.includes('{{confirmUrl}}') && errors.push(key + ': Missing {{confirmUrl}}'); + + } else if (key === 'layout') { + + let layout = values[index]; + let err = hasMjmlError('', layout); + + err && errors.push('layout: ' + (err.message || err)); + !layout.includes('{{{body}}}') && errors.push('layout: {{{body}}} not found'); + } + }); + + + if (errors.length) { + errors.forEach((err, index) => { + errors[index] = (index + 1) + ') ' + err; + }); + return 'Please fix these MJML errors:\n\n' + errors.join('\n'); + } + + return null; +} diff --git a/lib/models/links.js b/lib/models/links.js index 7beeecd3..e7ce5591 100644 --- a/lib/models/links.js +++ b/lib/models/links.js @@ -3,6 +3,7 @@ let db = require('../db'); let shortid = require('shortid'); let util = require('util'); +let _ = require('../translate')._; let geoip = require('geoip-ultralight'); let campaigns = require('./campaigns'); @@ -11,41 +12,40 @@ let lists = require('./lists'); let log = require('npmlog'); let urllib = require('url'); +let he = require('he'); +let ua_parser = require('device'); -module.exports.resolve = (campaignCid, linkCid, callback) => { - campaigns.getByCid(campaignCid, (err, campaign) => { +module.exports.resolve = (linkCid, callback) => { + db.getConnection((err, connection) => { if (err) { return callback(err); } - if (!campaign) { - return callback('Campaign not found'); - } - db.getConnection((err, connection) => { + let query = 'SELECT id, url FROM links WHERE `cid`=? LIMIT 1'; + connection.query(query, [linkCid], (err, rows) => { + connection.release(); if (err) { return callback(err); } - let query = 'SELECT id, url FROM links WHERE `campaign`=? AND `cid`=? LIMIT 1'; - connection.query(query, [campaign.id, linkCid], (err, rows) => { - connection.release(); - if (err) { - return callback(err); - } - if (rows && rows.length) { - return callback(null, rows[0].id, rows[0].url); - } + if (rows && rows.length) { + return callback(null, rows[0].id, rows[0].url); + } - return callback(null, false); - }); + return callback(null, false); }); }); }; -module.exports.countClick = (remoteIp, campaignCid, listCid, subscriptionCid, linkId, callback) => { +module.exports.countClick = (remoteIp, useragent, campaignCid, listCid, subscriptionCid, linkId, callback) => { getSubscriptionData(campaignCid, listCid, subscriptionCid, (err, data) => { if (err) { return callback(err); } + + if (!data || data.campaign.clickTrackingDisabled) { + return callback(null, false); + } + db.getConnection((err, connection) => { if (err) { return callback(err); @@ -53,13 +53,14 @@ module.exports.countClick = (remoteIp, campaignCid, listCid, subscriptionCid, li connection.beginTransaction(err => { if (err) { + connection.release(); return callback(err); } let country = geoip.lookupCountry(remoteIp) || null; - - let query = 'INSERT INTO `campaign_tracker__' + data.campaign.id + '` (`list`, `subscriber`, `link`, `ip`, `country`) VALUES (?,?,?,?,?) ON DUPLICATE KEY UPDATE `count`=`count`+1'; - connection.query(query, [data.list.id, data.subscription.id, linkId, remoteIp, country], (err, result) => { + let device = ua_parser(useragent, { unknownUserAgentDeviceType: 'desktop', emptyUserAgentDeviceType: 'desktop' }); + let query = 'INSERT INTO `campaign_tracker__' + data.campaign.id + '` (`list`, `subscriber`, `link`, `ip`, `device_type`, `country`) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `count`=`count`+1'; + connection.query(query, [data.list.id, data.subscription.id, linkId, remoteIp, device.type, country], (err, result) => { if (err && err.code !== 'ER_DUP_ENTRY') { return connection.rollback(() => { connection.release(); @@ -98,8 +99,8 @@ module.exports.countClick = (remoteIp, campaignCid, listCid, subscriptionCid, li }); } - let query = 'INSERT INTO `campaign_tracker__' + data.campaign.id + '` (`list`, `subscriber`, `link`, `ip`, `country`) VALUES (?,?,?,?,?)'; - connection.query(query, [data.list.id, data.subscription.id, 0, remoteIp, country], err => { + let query = 'INSERT INTO `campaign_tracker__' + data.campaign.id + '` (`list`, `subscriber`, `link`, `ip`, `device_type`, `country`) VALUES (?,?,?,?,?,?)'; + connection.query(query, [data.list.id, data.subscription.id, 0, remoteIp, device.type, country], err => { if (err && err.code !== 'ER_DUP_ENTRY') { return connection.rollback(() => { connection.release(); @@ -141,7 +142,7 @@ module.exports.countClick = (remoteIp, campaignCid, listCid, subscriptionCid, li }); // also count clicks as open events in case beacon image was blocked - module.exports.countOpen(remoteIp, campaignCid, listCid, subscriptionCid, () => false); + module.exports.countOpen(remoteIp, useragent, campaignCid, listCid, subscriptionCid, () => false); }); }); }); @@ -151,12 +152,16 @@ module.exports.countClick = (remoteIp, campaignCid, listCid, subscriptionCid, li }); }; -module.exports.countOpen = (remoteIp, campaignCid, listCid, subscriptionCid, callback) => { +module.exports.countOpen = (remoteIp, useragent, campaignCid, listCid, subscriptionCid, callback) => { getSubscriptionData(campaignCid, listCid, subscriptionCid, (err, data) => { if (err) { return callback(err); } + if (!data || data.campaign.openTrackingDisabled) { + return callback(null, false); + } + db.getConnection((err, connection) => { if (err) { return callback(err); @@ -164,13 +169,14 @@ module.exports.countOpen = (remoteIp, campaignCid, listCid, subscriptionCid, cal connection.beginTransaction(err => { if (err) { + connection.release(); return callback(err); } let country = geoip.lookupCountry(remoteIp) || null; - - let query = 'INSERT INTO `campaign_tracker__' + data.campaign.id + '` (`list`, `subscriber`, `link`, `ip`, `country`) VALUES (?,?,?,?,?) ON DUPLICATE KEY UPDATE `count`=`count`+1'; - connection.query(query, [data.list.id, data.subscription.id, -1, remoteIp, country], (err, result) => { + let device = ua_parser(useragent, { unknownUserAgentDeviceType: 'desktop', emptyUserAgentDeviceType: 'desktop' }); + let query = 'INSERT INTO `campaign_tracker__' + data.campaign.id + '` (`list`, `subscriber`, `link`, `ip`, `device_type`, `country`) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `count`=`count`+1'; + connection.query(query, [data.list.id, data.subscription.id, -1, remoteIp, device.type, country], (err, result) => { if (err && err.code !== 'ER_DUP_ENTRY') { return connection.rollback(() => { connection.release(); @@ -262,50 +268,64 @@ module.exports.add = (url, campaignId, callback) => { }; module.exports.updateLinks = (campaign, list, subscription, serviceUrl, message, callback) => { - let re = /(]* href\s*=[\s"']*)(http[^"'>\s]+)/gi; - let urls = new Set(); - message.replace(re, (match, prefix, url) => { - urls.add(url); - }); - - let map = new Map(); - let vals = urls.values(); - - // insert tracking image - let inserted = false; - let imgUrl = urllib.resolve(serviceUrl, util.format('/links/%s/%s/%s', campaign.cid, list.cid, encodeURIComponent(subscription.cid))); - let img = ''; - message = message.replace(/<\/body\b/i, match => { - inserted = true; - return img + match; - }); - if (!inserted) { - message = message + img; + if ((campaign.openTrackingDisabled && campaign.clickTrackingDisabled) || !message || !message.trim()) { + // tracking is disabled, do not modify the message + return setImmediate(() => callback(null, message)); } - - let replaceUrls = () => { - callback(null, - message.replace(re, (match, prefix, url) => - prefix + (map.has(url) ? urllib.resolve(serviceUrl, util.format('/links/%s/%s/%s/%s', campaign.cid, list.cid, encodeURIComponent(subscription.cid), encodeURIComponent(map.get(url)))) : url))); - }; - - let storeNext = () => { - let urlItem = vals.next(); - if (urlItem.done) { - return replaceUrls(); - } - - module.exports.add(urlItem.value, campaign.id, (err, linkId, cid) => { - if (err) { - log.error('Link', err.stack); - return storeNext(); - } - map.set(urlItem.value, cid); - return storeNext(); + + // insert tracking image + if (!campaign.openTrackingDisabled) { + let inserted = false; + let imgUrl = urllib.resolve(serviceUrl, util.format('/links/%s/%s/%s', campaign.cid, list.cid, encodeURIComponent(subscription.cid))); + let img = 'mt'; + message = message.replace(/<\/body\b/i, match => { + inserted = true; + return img + match; + }); + if (!inserted) { + message = message + img; + } + if (campaign.clickTrackingDisabled) { + return callback(null, message); + } + } + + if (!campaign.clickTrackingDisabled) { + let re = /(]* href\s*=[\s"']*)(http[^"'>\s]+)/gi; + let urls = new Set(); + (message || '').replace(re, (match, prefix, url) => { + urls.add(url); }); - }; - storeNext(); + let map = new Map(); + let vals = urls.values(); + + let replaceUrls = () => { + callback(null, + message.replace(re, (match, prefix, url) => + prefix + (map.has(url) ? urllib.resolve(serviceUrl, util.format('/links/%s/%s/%s/%s', campaign.cid, list.cid, encodeURIComponent(subscription.cid), encodeURIComponent(map.get(url)))) : url))); + }; + + let storeNext = () => { + let urlItem = vals.next(); + if (urlItem.done) { + return replaceUrls(); + } + + module.exports.add(he.decode(urlItem.value, { + isAttributeValue: true + }), campaign.id, (err, linkId, cid) => { + if (err) { + log.error('Link', err); + return storeNext(); + } + map.set(urlItem.value, cid); + return storeNext(); + }); + }; + + storeNext(); + } }; function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) { @@ -314,7 +334,7 @@ function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) { return callback(err); } if (!campaign) { - return callback(new Error('Campaign not found')); + return callback(new Error(_('Campaign not found'))); } lists.getByCid(listCid, (err, list) => { @@ -322,7 +342,7 @@ function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) { return callback(err); } if (!list) { - return callback(new Error('Campaign not found')); + return callback(new Error(_('List not found'))); } subscriptions.get(list.id, subscriptionCid, (err, subscription) => { @@ -330,7 +350,7 @@ function getSubscriptionData(campaignCid, listCid, subscriptionCid, callback) { return callback(err); } if (!subscription) { - return callback(new Error('Subscription not found')); + return callback(new Error(_('Subscription not found'))); } return callback(null, { diff --git a/lib/models/lists.js b/lib/models/lists.js index c464b702..6c0a498a 100644 --- a/lib/models/lists.js +++ b/lib/models/lists.js @@ -4,29 +4,33 @@ let db = require('../db'); let tools = require('../tools'); let shortid = require('shortid'); let segments = require('./segments'); +let subscriptions = require('./subscriptions'); +let _ = require('../translate')._; +let tableHelpers = require('../table-helpers'); -let allowedKeys = ['description']; +const UnsubscriptionMode = { + ONE_STEP: 0, + ONE_STEP_WITH_FORM: 1, + TWO_STEP: 2, + TWO_STEP_WITH_FORM: 3, + MANUAL: 4, + MAX: 5 +}; + +module.exports.UnsubscriptionMode = UnsubscriptionMode; + +let allowedKeys = ['description', 'default_form', 'public_subscribe', 'unsubscription_mode', 'listunsubscribe_disabled']; module.exports.list = (start, limit, callback) => { - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } + tableHelpers.list('lists', ['*'], 'name', null, start, limit, callback); +}; - connection.query('SELECT SQL_CALC_FOUND_ROWS * FROM lists ORDER BY name LIMIT ? OFFSET ?', [limit, start], (err, rows) => { - if (err) { - connection.release(); - return callback(err); - } - connection.query('SELECT FOUND_ROWS() AS total', (err, total) => { - connection.release(); - if (err) { - return callback(err); - } - return callback(null, rows, total && total[0] && total[0].total); - }); - }); - }); +module.exports.filter = (request, parent, callback) => { + tableHelpers.filter('lists', ['*'], request, ['#', 'name', 'cid', 'subscribers', 'description'], ['name'], 'name ASC', null, callback); +}; + +module.exports.filterQuicklist = (request, callback) => { + tableHelpers.filter('lists', ['id', 'name', 'subscribers'], request, ['#', 'name', 'subscribers'], ['name'], 'name ASC', null, callback); }; module.exports.quicklist = callback => { @@ -61,6 +65,37 @@ module.exports.quicklist = callback => { }); }; +module.exports.getListsWithEmail = (email, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT id, name FROM lists', (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + let lists = (rows || []).map(tools.convertKeys); + const results = []; + lists.forEach((list, index, arr) => { + subscriptions.getByEmail(list.id, email, (err, sub) => { + if (err) { + return callback(err); + } + if (sub) { + results.push(list.id); + } + if (index === arr.length - 1) { + return callback(null, lists.filter(list => results.includes(list.id))); + } + }); + }); + }); + }); +}; + module.exports.getByCid = (cid, callback) => { resolveCid(cid, (err, id) => { if (err) { @@ -77,7 +112,7 @@ module.exports.get = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } db.getConnection((err, connection) => { @@ -107,21 +142,84 @@ module.exports.get = (id, callback) => { }); }; +module.exports.update = (id, updates, callback) => { + updates = updates || {}; + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing List ID'))); + } + + const data = tools.convertKeys(updates); + + const keys = []; + const values = []; + + // The update can be only partial when executed from forms/:list + if (!data.customFormChangeOnly) { + data.publicSubscribe = data.publicSubscribe ? 1 : 0; + data.listunsubscribeDisabled = data.listunsubscribeDisabled ? 1 : 0; + data.unsubscriptionMode = Number(data.unsubscriptionMode); + + let name = (data.name || '').toString().trim(); + + if (!name) { + return callback(new Error(_('List Name must be set'))); + } + + keys.push('name'); + values.push(name); + } + + Object.keys(data).forEach(key => { + let value = data[key].toString().trim(); + key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } + if (allowedKeys.indexOf(key) >= 0) { + keys.push(key); + values.push(value); + } + }); + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + values.push(id); + + connection.query('UPDATE lists SET ' + keys.map(key => key + '=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, result && result.affectedRows || false); + }); + }); +}; + module.exports.create = (list, callback) => { let data = tools.convertKeys(list); + data.publicSubscribe = data.publicSubscribe ? 1 : 0; + let name = (data.name || '').toString().trim(); if (!data) { - return callback(new Error('List Name must be set')); + return callback(new Error(_('List Name must be set'))); } let keys = ['name']; let values = [name]; - Object.keys(list).forEach(key => { - let value = list[key].trim(); + Object.keys(data).forEach(key => { + let value = data[key].toString().trim(); key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } if (allowedKeys.indexOf(key) >= 0) { keys.push(key); values.push(value); @@ -160,55 +258,11 @@ module.exports.create = (list, callback) => { }); }; -module.exports.update = (id, updates, callback) => { - updates = updates || {}; - id = Number(id) || 0; - - let data = tools.convertKeys(updates); - - let name = (data.name || '').toString().trim(); - let keys = ['name']; - let values = [name]; - - if (id < 1) { - return callback(new Error('Missing List ID')); - } - - if (!name) { - return callback(new Error('List Name must be set')); - } - - Object.keys(updates).forEach(key => { - let value = updates[key].trim(); - key = tools.toDbKey(key); - if (allowedKeys.indexOf(key) >= 0) { - keys.push(key); - values.push(value); - } - }); - - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - - values.push(id); - - connection.query('UPDATE lists SET ' + keys.map(key => key + '=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => { - connection.release(); - if (err) { - return callback(err); - } - return callback(null, result && result.affectedRows || false); - }); - }); -}; - module.exports.delete = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } db.getConnection((err, connection) => { @@ -237,7 +291,7 @@ module.exports.delete = (id, callback) => { function resolveCid(cid, callback) { cid = (cid || '').toString().trim(); if (!cid) { - return callback(new Error('Missing List CID')); + return callback(new Error(_('Missing List CID'))); } db.getConnection((err, connection) => { diff --git a/lib/models/report-templates.js b/lib/models/report-templates.js new file mode 100644 index 00000000..6dcb2c70 --- /dev/null +++ b/lib/models/report-templates.js @@ -0,0 +1,161 @@ +'use strict'; + +const db = require('../db'); +const tableHelpers = require('../table-helpers'); +const tools = require('../tools'); +const _ = require('../translate')._; + +const allowedKeys = ['name', 'description', 'mime_type', 'user_fields', 'js', 'hbs']; + +module.exports.list = (start, limit, callback) => { + tableHelpers.list('report_templates', ['*'], 'name', null, start, limit, callback); +}; + +module.exports.quicklist = callback => { + tableHelpers.quicklist('report_templates', ['id', 'name'], 'name', callback); +}; + +module.exports.filter = (request, callback) => { + tableHelpers.filter('report_templates', ['*'], request, ['#', 'name', 'description', 'created'], ['name'], 'created DESC', null, callback); +}; + +module.exports.get = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing report template ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM report_templates WHERE id=?', [id], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, false); + } + + const template = tools.convertKeys(rows[0]); + + const userFields = template.userFields.trim(); + if (userFields !== '') { + try { + template.userFieldsObject = JSON.parse(userFields); + } catch (err) { + // This is to handle situation when for some reason we get corrupted JSON in the DB. + template.userFieldsObject = {}; + template.userFields = '{}'; + } + } else { + template.userFieldsObject = {}; + } + + return callback(null, template); + }); + }); +}; + +module.exports.createOrUpdate = (createMode, data, callback) => { + data = data || {}; + + const id = 'id' in data ? Number(data.id) : 0; + + if (!createMode && id < 1) { + return callback(new Error(_('Missing report template ID'))); + } + + const template = tools.convertKeys(data); + const name = (template.name || '').toString().trim(); + + if (!name) { + return callback(new Error(_('Report template name must be set'))); + } + + const keys = ['name']; + const values = [name]; + + Object.keys(template).forEach(key => { + let value = typeof template[key] === 'number' ? template[key] : (template[key] || '').toString().trim(); + key = tools.toDbKey(key); + + if (key === 'description') { + value = tools.purifyHTML(value); + } + + if (key === 'user_fields') { + value = value.trim(); + + if (value !== '') { + try { + JSON.parse(value); + } catch (err) { + return callback(err); + } + } + } + + if (allowedKeys.indexOf(key) >= 0 && keys.indexOf(key) < 0) { + keys.push(key); + values.push(value); + } + }); + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query; + + if (createMode) { + query = 'INSERT INTO report_templates (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')'; + } else { + query = 'UPDATE report_templates SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1'; + values.push(id); + } + + connection.query(query, values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + if (createMode) { + return callback(null, result && result.insertId || false); + } else { + return callback(null, result && result.affectedRows || false); + } + }); + }); +}; + +module.exports.delete = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing report template ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('DELETE FROM report_templates WHERE id=? LIMIT 1', [id], (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + const affected = result && result.affectedRows || 0; + return callback(err, affected); + }); + }); +}; + diff --git a/lib/models/reports.js b/lib/models/reports.js new file mode 100644 index 00000000..edfe665f --- /dev/null +++ b/lib/models/reports.js @@ -0,0 +1,262 @@ +'use strict'; + +const db = require('../db'); +const tableHelpers = require('../table-helpers'); +const fields = require('./fields'); +const reportTemplates = require('./report-templates'); +const tools = require('../tools'); +const _ = require('../translate')._; + +const allowedKeys = ['name', 'description', 'report_template', 'params']; + +const ReportState = { + SCHEDULED: 0, + PROCESSING: 1, + FINISHED: 2, + FAILED: 3, + MAX: 4 +}; + +module.exports.ReportState = ReportState; + +module.exports.list = (start, limit, callback) => { + tableHelpers.list('reports', ['*'], 'name', null, start, limit, callback); +}; + +module.exports.listWithState = (state, start, limit, callback) => { + tableHelpers.list('reports', ['*'], 'name', { where: 'state=?', values: [state] }, start, limit, callback); +}; + +module.exports.filter = (request, callback) => { + tableHelpers.filter('reports JOIN report_templates ON reports.report_template = report_templates.id', + ['reports.id AS id', 'reports.name AS name', 'reports.description AS description', 'reports.state AS state', 'reports.report_template AS report_template', 'reports.params AS params', 'reports.last_run AS last_run', 'report_templates.name AS report_template_name', 'report_templates.mime_type AS mime_type' ], + request, ['#', 'name', 'report_templates.name', 'description', 'last_run'], ['name'], 'name ASC', null, callback); +}; + +module.exports.get = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing report ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM reports WHERE id=?', [id], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, false); + } + + const template = tools.convertKeys(rows[0]); + + const params = template.params.trim(); + if (params !== '') { + try { + template.paramsObject = JSON.parse(params); + } catch (err) { + return callback(err); + } + } else { + template.params = {}; + } + + return callback(null, template); + }); + }); +}; + +// This method is not supposed to be used for unsanitized inputs. It does not do any checks. +module.exports.updateFields = (id, fieldValueMap, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + const clauses = []; + const values = []; + for (let key of Object.keys(fieldValueMap)) { + clauses.push(tools.toDbKey(key) + '=?'); + values.push(fieldValueMap[key]); + } + + values.push(id); + + const query = 'UPDATE reports SET ' + clauses.join(', ') + ' WHERE id=? LIMIT 1'; + connection.query(query, values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + return callback(null, result && result.affectedRows || false); + }); + }); +}; + +module.exports.createOrUpdate = (createMode, report, callback) => { + report = report || {}; + + const id = 'id' in report ? Number(report.id) : 0; + + if (!createMode && id < 1) { + return callback(new Error(_('Missing report ID'))); + } + + const name = (report.name || '').toString().trim(); + + if (!name) { + return callback(new Error(_('Report name must be set'))); + } + + const reportTemplateId = Number(report.reportTemplate); + reportTemplates.get(reportTemplateId, (err, reportTemplate) => { + if (err) { + return callback(err); + } + + const params = report.paramsObject; + for (const spec of reportTemplate.userFieldsObject) { + if (params[spec.id].length < spec.minOccurences) { + return callback(new Error(_('At least ' + spec.minOccurences + ' rows in "' + spec.name + '" have to be selected.'))); + } + + if (params[spec.id].length > spec.maxOccurences) { + return callback(new Error(_('At most ' + spec.minOccurences + ' rows in "' + spec.name + '" can be selected.'))); + } + } + + const keys = ['name', 'params']; + const values = [name, JSON.stringify(params)]; + + + Object.keys(report).forEach(key => { + let value = typeof report[key] === 'number' ? report[key] : (report[key] || '').toString().trim(); + key = tools.toDbKey(key); + + if (key === 'description') { + value = tools.purifyHTML(value); + } + + if (allowedKeys.indexOf(key) >= 0 && keys.indexOf(key) < 0) { + keys.push(key); + values.push(value); + } + }); + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query; + + if (createMode) { + query = 'INSERT INTO reports (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(',') + ')'; + } else { + query = 'UPDATE reports SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE id=? LIMIT 1'; + values.push(id); + } + + connection.query(query, values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + if (createMode) { + return callback(null, result && result.insertId || false); + } else { + return callback(null, result && result.affectedRows || false); + } + }); + }); + }); +}; + +module.exports.delete = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing report ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('DELETE FROM reports WHERE id=? LIMIT 1', [id], (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + const affected = result && result.affectedRows || 0; + return callback(err, affected); + }); + }); +}; + +const campaignFieldsMapping = { + tracker_count: 'tracker.count', + country: 'tracker.country', + device_type: 'tracker.device_type', + status: 'campaign.status', + first_name: 'subscribers.first_name', + last_name: 'subscribers.last_name', + email: 'subscribers.email' +}; + +module.exports.getCampaignResults = (campaign, select, clause, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + fields.list(campaign.list, (err, fieldList) => { + if (err) { + return callback(err); + } + + const fieldsMapping = fieldList.reduce((map, field) => { + map[customFieldName(field.key)] = 'subscribers.' + field.column; + return map; + }, Object.assign({}, campaignFieldsMapping)); + + let selFields = []; + for (let idx = 0; idx < select.length; idx++) { + const item = select[idx]; + if (item in fieldsMapping) { + selFields.push(fieldsMapping[item] + ' AS ' + item); + } else if (item === '*') { + selFields = selFields.concat(Object.keys(fieldsMapping).map(item => fieldsMapping[item] + ' AS ' + item)); + } else { + selFields.push(item); + } + } + + const query = 'SELECT ' + selFields.join(', ') + ' FROM `subscription__' + campaign.list + '` subscribers INNER JOIN `campaign__' + campaign.id + '` campaign on subscribers.id=campaign.subscription LEFT JOIN `campaign_tracker__' + campaign.id + '` tracker on subscribers.id=tracker.subscriber ' + clause; + + connection.query(query, (err, results) => { + connection.release(); + if (err) { + return callback(err); + } + + return callback(null, results); + }); + }); + }); +}; + +function customFieldName(id) { + return id.replace(/MERGE_/, 'CUSTOM_').toLowerCase(); +} diff --git a/lib/models/segments.js b/lib/models/segments.js index 63025fb8..a2e891f9 100644 --- a/lib/models/segments.js +++ b/lib/models/segments.js @@ -3,34 +3,36 @@ let tools = require('../tools'); let db = require('../db'); let fields = require('./fields'); +let util = require('util'); +let _ = require('../translate')._; module.exports.defaultColumns = [{ column: 'email', - name: 'Email address', + name: _('Email address'), type: 'string' }, { column: 'opt_in_country', - name: 'Signup country', + name: _('Signup country'), type: 'string' }, { column: 'created', - name: 'Sign up date', + name: _('Sign up date'), type: 'date' }, { column: 'latest_open', - name: 'Latest open', + name: _('Latest open'), type: 'date' }, { column: 'latest_click', - name: 'Latest click', + name: _('Latest click'), type: 'date' }, { column: 'first_name', - name: 'First name', + name: _('First name'), type: 'string' }, { column: 'last_name', - name: 'Last name', + name: _('Last name'), type: 'string' }]; @@ -38,7 +40,7 @@ module.exports.list = (listId, callback) => { listId = Number(listId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } @@ -64,7 +66,7 @@ module.exports.get = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Segment ID')); + return callback(new Error(_('Missing Segment ID'))); } db.getConnection((err, connection) => { @@ -80,7 +82,7 @@ module.exports.get = (id, callback) => { } if (!rows || !rows.length) { connection.release(); - return callback(new Error('Segment not found')); + return callback(new Error(_('Segment not found'))); } let segment = tools.convertKeys(rows[0]); @@ -140,17 +142,21 @@ module.exports.get = (id, callback) => { case 'number': case 'date': case 'birthday': - if (rule.value.range) { + if (rule.value.relativeRange) { + let startString = rule.value.startDirection ? util.format(_('%s days after today'), rule.value.start) : util.format(_('%s days before today'), rule.value.start); + let endString = rule.value.endDirection ? util.format(_('%s days after today'), rule.value.end) : util.format(_('%s days before today'), rule.value.end); + rule.formatted = (rule.value.start ? startString : _('today')) + ' … ' + (rule.value.end ? endString : _('today')); + } else if (rule.value.range) { rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || ''); } else { rule.formatted = rule.value.value || ''; } break; case 'boolean': - rule.formatted = rule.value.value ? 'Selected' : 'Not selected'; + rule.formatted = rule.value.value ? _('Selected') : _('Not selected'); break; default: - rule.formatted = rule.value.value || ''; + rule.formatted = (rule.value.negate ? '!= ' : '') + (rule.value.value || ''); } return rule; @@ -167,7 +173,7 @@ module.exports.create = (listId, segment, callback) => { listId = Number(listId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } segment = tools.convertKeys(segment); @@ -176,11 +182,11 @@ module.exports.create = (listId, segment, callback) => { segment.type = Number(segment.type) || 0; if (!segment.name) { - return callback(new Error('Field Name must be set')); + return callback(new Error(_('Field Name must be set'))); } if (segment.type <= 0) { - return callback(new Error('Invalid segment rule type')); + return callback(new Error(_('Invalid segment rule type'))); } let keys = ['list', 'name', 'type']; @@ -207,7 +213,7 @@ module.exports.update = (id, updates, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Segment ID')); + return callback(new Error(_('Missing Segment ID'))); } let segment = tools.convertKeys(updates); @@ -216,11 +222,11 @@ module.exports.update = (id, updates, callback) => { segment.type = Number(segment.type) || 0; if (!segment.name) { - return callback(new Error('Field Name must be set')); + return callback(new Error(_('Field Name must be set'))); } if (segment.type <= 0) { - return callback(new Error('Invalid segment rule type')); + return callback(new Error(_('Invalid segment rule type'))); } let keys = ['name', 'type']; @@ -247,7 +253,7 @@ module.exports.delete = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Segment ID')); + return callback(new Error(_('Missing Segment ID'))); } db.getConnection((err, connection) => { @@ -269,7 +275,7 @@ module.exports.createRule = (segmentId, rule, callback) => { segmentId = Number(segmentId) || 0; if (segmentId < 1) { - return callback(new Error('Missing Segment ID')); + return callback(new Error(_('Missing Segment ID'))); } rule = tools.convertKeys(rule); @@ -280,12 +286,12 @@ module.exports.createRule = (segmentId, rule, callback) => { } if (!segment) { - return callback(new Error('Selected segment not found')); + return callback(new Error(_('Selected segment not found'))); } let column = segment.columns.filter(column => column.column === rule.column).pop(); if (!column) { - return callback(new Error('Invalid rule type')); + return callback(new Error(_('Invalid rule type'))); } let value; @@ -294,7 +300,15 @@ module.exports.createRule = (segmentId, rule, callback) => { case 'date': case 'birthday': case 'number': - if (rule.range) { + if (column.type === 'date' && rule.range === 'relative') { + value = { + relativeRange: true, + start: Number(rule.startRelative) || 0, + startDirection: Number(rule.startDirection) ? 1 : 0, + end: Number(rule.endRelative) || 0, + endDirection: Number(rule.endDirection) ? 1 : 0 + }; + } else if (rule.range === 'yes') { value = { range: true, start: rule.start, @@ -313,6 +327,7 @@ module.exports.createRule = (segmentId, rule, callback) => { break; default: value = { + negate: Number(rule.negate) ? 1 : 0, value: rule.value }; } @@ -341,7 +356,7 @@ module.exports.getRule = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Rule ID')); + return callback(new Error(_('Missing Rule ID'))); } db.getConnection((err, connection) => { @@ -357,7 +372,7 @@ module.exports.getRule = (id, callback) => { } if (!rows || !rows.length) { - return callback(new Error('Specified rule not found')); + return callback(new Error(_('Specified rule not found'))); } let rule = tools.convertKeys(rows[0]); @@ -368,7 +383,7 @@ module.exports.getRule = (id, callback) => { } if (!segment) { - return callback(new Error('Specified segment not found')); + return callback(new Error(_('Specified segment not found'))); } if (rule.value) { @@ -389,17 +404,22 @@ module.exports.getRule = (id, callback) => { case 'number': case 'date': case 'birthday': - if (rule.value.range) { + if (rule.value.relativeRange) { + + let startString = rule.value.startDirection ? util.format(_('%s days after today'), rule.value.start) : util.format(_('%s days before today'), rule.value.start); + let endString = rule.value.endDirection ? util.format(_('%s days after today'), rule.value.end) : util.format(_('%s days before today'), rule.value.end); + rule.formatted = (rule.value.start ? startString : _('today')) + ' … ' + (rule.value.end ? endString : _('today')); + } else if (rule.value.range) { rule.formatted = (rule.value.start || '') + ' … ' + (rule.value.end || ''); } else { rule.formatted = rule.value.value || ''; } break; case 'boolean': - rule.formatted = rule.value.value ? 'Selected' : 'Not selected'; + rule.formatted = rule.value.value ? _('Selected') : _('Not selected'); break; default: - rule.formatted = rule.value.value || ''; + rule.formatted = (rule.value.negate ? '!= ' : '') + (rule.value.value || ''); } return callback(null, rule); @@ -412,7 +432,7 @@ module.exports.updateRule = (id, rule, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Rule ID')); + return callback(new Error(_('Missing Rule ID'))); } rule = tools.convertKeys(rule); @@ -423,7 +443,7 @@ module.exports.updateRule = (id, rule, callback) => { } if (!existingRule) { - return callback(new Error('Selected rule not found')); + return callback(new Error(_('Selected rule not found'))); } module.exports.get(existingRule.segment, (err, segment) => { @@ -432,12 +452,12 @@ module.exports.updateRule = (id, rule, callback) => { } if (!segment) { - return callback(new Error('Selected segment not found')); + return callback(new Error(_('Selected segment not found'))); } let column = segment.columns.filter(column => column.column === existingRule.column).pop(); if (!column) { - return callback(new Error('Invalid rule type')); + return callback(new Error(_('Invalid rule type'))); } let value; @@ -445,7 +465,15 @@ module.exports.updateRule = (id, rule, callback) => { case 'date': case 'birthday': case 'number': - if (rule.range) { + if (column.type === 'date' && rule.range === 'relative') { + value = { + relativeRange: true, + start: Number(rule.startRelative) || 0, + startDirection: Number(rule.startDirection) ? 1 : 0, + end: Number(rule.endRelative) || 0, + endDirection: Number(rule.endDirection) ? 1 : 0 + }; + } else if (rule.range === 'yes') { value = { range: true, start: rule.start, @@ -464,6 +492,7 @@ module.exports.updateRule = (id, rule, callback) => { break; default: value = { + negate: Number(rule.negate) ? 1 : 0, value: rule.value }; } @@ -494,7 +523,7 @@ module.exports.deleteRule = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Rule ID')); + return callback(new Error(_('Missing Rule ID'))); } db.getConnection((err, connection) => { @@ -512,19 +541,26 @@ module.exports.deleteRule = (id, callback) => { }); }; -module.exports.getQuery = (id, callback) => { +module.exports.getQuery = (id, prefix, callback) => { module.exports.get(id, (err, segment) => { if (err) { return callback(err); } if (!segment) { - return callback(new Error('Segment not found')); + return callback(new Error(_('Segment not found'))); } + prefix = prefix ? prefix + '.' : ''; + let query = []; let values = []; + let getRelativeDate = (days, direction) => { + let date = new Date(Date.now() + (direction ? 1 : -1) * days * 24 * 3600 * 1000); + return date.toISOString().substr(0, 10); + }; + let getDate = (value, nextDay) => { let parts = value.trim().split(/\D/); let year = Number(parts.shift()) || 0; @@ -538,47 +574,70 @@ module.exports.getQuery = (id, callback) => { segment.rules.forEach(rule => { switch (rule.columnType.type) { - case 'string': - query.push('`' + rule.columnType.column + '` LIKE ?'); + case 'string': { + let condition = rule.value.negate ? 'NOT LIKE' : 'LIKE'; + query.push(prefix + '`' + rule.columnType.column + '` ' + condition + ' ?'); values.push(rule.value.value); break; + } case 'boolean': - query.push('`' + rule.columnType.column + '` = ?'); + query.push(prefix + '`' + rule.columnType.column + '` = ?'); values.push(rule.value.value); break; case 'number': if (rule.value.range) { - query.push('`' + rule.columnType.column + '` >= ?'); - query.push('`' + rule.columnType.column + '` < ?'); - values.push(rule.value.start); - values.push(rule.value.end); + let ruleval = ''; + if (rule.value.start) { + ruleval = prefix + '`' + rule.columnType.column + '` >= ?'; + values.push(rule.value.start); + } + if (rule.value.end) { + ruleval = (ruleval ? '(' + ruleval + ' AND ' : '') + prefix + '`' + rule.columnType.column + '` < ?' + (ruleval ? ')' : ''); + values.push(rule.value.end); + } + if (ruleval) { + query.push(ruleval); + } } else { - query.push('`' + rule.columnType.column + '` = ?'); + query.push(prefix + '`' + rule.columnType.column + '` = ?'); values.push(rule.value.value); } break; case 'birthday': if (rule.value.range) { - query.push('`' + rule.columnType.column + '` >= ?'); - query.push('`' + rule.columnType.column + '` < ?'); - values.push(getDate('2000-' + rule.value.start)); - values.push(getDate('2000-' + rule.value.end, true)); + let start = rule.value.start || '01-01'; + let end = rule.value.end || '12-31'; + query.push('(' + prefix + '`' + rule.columnType.column + '` >= ? AND ' + prefix + '`' + rule.columnType.column + '` < ?)'); + values.push(getDate('2000-' + start)); + values.push(getDate('2000-' + end, true)); } else { - query.push('`' + rule.columnType.column + '` >= ?'); - query.push('`' + rule.columnType.column + '` < ?'); + query.push('(' + prefix + '`' + rule.columnType.column + '` >= ? AND ' + prefix + '`' + rule.columnType.column + '` < ?)'); values.push(getDate('2000-' + rule.value.value)); values.push(getDate('2000-' + rule.value.value, true)); } break; case 'date': - if (rule.value.range) { - query.push('`' + rule.columnType.column + '` >= ?'); - query.push('`' + rule.columnType.column + '` < ?'); - values.push(getDate(rule.value.start)); - values.push(getDate(rule.value.end, true)); + if (rule.value.relativeRange) { + query.push('(' + prefix + '`' + rule.columnType.column + '` >= ? AND ' + prefix + '`' + rule.columnType.column + '` < ?)'); + // start + values.push(getDate(getRelativeDate(rule.value.start, rule.value.startDirection))); + // end + values.push(getDate(getRelativeDate(rule.value.end, rule.value.endDirection), true)); + } else if (rule.value.range) { + let ruleval = ''; + if (rule.value.start) { + ruleval = prefix + '`' + rule.columnType.column + '` >= ?'; + values.push(getDate(rule.value.start)); + } + if (rule.value.end) { + ruleval = (ruleval ? '(' + ruleval + ' AND ' : '') + prefix + '`' + rule.columnType.column + '` < ?' + (ruleval ? ')' : ''); + values.push(getDate(rule.value.end, true)); + } + if (ruleval) { + query.push(ruleval); + } } else { - query.push('`' + rule.columnType.column + '` >= ?'); - query.push('`' + rule.columnType.column + '` < ?'); + query.push('(' + prefix + '`' + rule.columnType.column + '` >= ? AND ' + prefix + '`' + rule.columnType.column + '` < ?)'); values.push(getDate(rule.value.value)); values.push(getDate(rule.value.value, true)); } @@ -600,9 +659,9 @@ module.exports.subscribers = (id, onlySubscribed, callback) => { return callback(err); } if (!segment) { - return callback(new Error('Segment not found')); + return callback(new Error(_('Segment not found'))); } - module.exports.getQuery(id, (err, queryData) => { + module.exports.getQuery(id, false, (err, queryData) => { if (err) { return callback(err); } diff --git a/lib/models/settings.js b/lib/models/settings.js index 77b9f391..9655ebd7 100644 --- a/lib/models/settings.js +++ b/lib/models/settings.js @@ -15,6 +15,8 @@ function listValues(filter, callback) { filter = false; } + // TODO: It would be good to cache the settings. It feels awkward to always go to DB to retrieve something what is essentially a constant + filter = [].concat(filter || []).map(key => tools.toDbKey(key)); db.getConnection((err, connection) => { diff --git a/lib/models/subscriptions.js b/lib/models/subscriptions.js index cae7cd8b..3a9a7f58 100644 --- a/lib/models/subscriptions.js +++ b/lib/models/subscriptions.js @@ -2,10 +2,23 @@ let db = require('../db'); let shortid = require('shortid'); +let striptags = require('striptags'); let tools = require('../tools'); +let helpers = require('../helpers'); let fields = require('./fields'); -let geoip = require('geoip-ultralight'); let segments = require('./segments'); +let _ = require('../translate')._; +let tableHelpers = require('../table-helpers'); + +const Status = { + SUBSCRIBED: 1, + UNSUBSCRIBED: 2, + BOUNCED: 3, + COMPLAINED: 4, + MAX: 5 +}; + +module.exports.Status = Status; module.exports.list = (listId, start, limit, callback) => { listId = Number(listId) || 0; @@ -13,25 +26,47 @@ module.exports.list = (listId, start, limit, callback) => { return callback(new Error('Missing List ID')); } + tableHelpers.list('subscription__' + listId, ['*'], 'email', null, start, limit, (err, rows, total) => { + if (!err) { + rows = rows.map(row => tools.convertKeys(row)); + } + return callback(err, rows, total); + }); +}; + +module.exports.listTestUsers = (listId, callback) => { + listId = Number(listId) || 0; + + if (listId < 1) { + return callback(new Error('Missing List ID')); + } + db.getConnection((err, connection) => { if (err) { return callback(err); } - connection.query('SELECT SQL_CALC_FOUND_ROWS * FROM `subscription__' + listId + '` ORDER BY email LIMIT ? OFFSET ?', [limit, start], (err, rows) => { + connection.query('SELECT id, cid, email, first_name, last_name FROM `subscription__' + listId + '` WHERE is_test=1 LIMIT 100', (err, rows) => { + connection.release(); if (err) { - connection.release(); return callback(err); } - connection.query('SELECT FOUND_ROWS() AS total', (err, total) => { - connection.release(); - if (err) { - return callback(err); - } - let subscriptions = rows.map(row => tools.convertKeys(row)); - return callback(null, subscriptions, total && total[0] && total[0].total); + if (!rows || !rows.length) { + return callback(null, []); + } + + let subscribers = rows.map(subscriber => { + subscriber = tools.convertKeys(subscriber); + let fullName = [].concat(subscriber.firstName || []).concat(subscriber.lastName || []).join(' '); + if (fullName) { + subscriber.displayName = fullName + ' <' + subscriber.email + '>'; + } else { + subscriber.displayName = subscriber.email; + } + return subscriber; }); + return callback(null, subscribers); }); }); }; @@ -41,183 +76,34 @@ module.exports.filter = (listId, request, columns, segmentId, callback) => { segmentId = Number(segmentId) || 0; if (!listId) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } - let processQuery = queryData => { - - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - - let query = 'SELECT COUNT(id) AS total FROM `subscription__' + listId + '`'; - let values = []; - - if (queryData.where) { - query += ' WHERE ' + queryData.where; - values = values.concat(queryData.values || []); - } - - connection.query(query, values, (err, total) => { - if (err) { - connection.release(); - return callback(err); - } - total = total && total[0] && total[0].total || 0; - - let ordering = []; - - if (request.order && request.order.length) { - - request.order.forEach(order => { - let orderField = columns[Number(order.column)]; - let orderDirection = (order.dir || '').toString().toLowerCase() === 'desc' ? 'DESC' : 'ASC'; - if (orderField) { - ordering.push('`' + orderField + '` ' + orderDirection); - } - }); - } - - if (!ordering.length) { - ordering.push('`email` ASC'); - } - - let args = [Number(request.length) || 50, Number(request.start) || 0]; - let query; - - if (request.search && request.search.value) { - query = 'SELECT SQL_CALC_FOUND_ROWS * FROM `subscription__' + listId + '` WHERE email LIKE ? OR first_name LIKE ? OR last_name LIKE ? ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?'; - - let searchVal = '%' + request.search.value.replace(/\\/g, '\\\\').replace(/([%_])/g, '\\$1') + '%'; - args = [searchVal, searchVal, searchVal].concat(queryData.values || []).concat(args); - } else { - query = 'SELECT SQL_CALC_FOUND_ROWS * FROM `subscription__' + listId + '` WHERE 1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?'; - args = [].concat(queryData.values || []).concat(args); - } - - connection.query(query, args, (err, rows) => { - if (err) { - connection.release(); - return callback(err); - } - connection.query('SELECT FOUND_ROWS() AS total', (err, filteredTotal) => { - connection.release(); - if (err) { - return callback(err); - } - - let subscriptions = rows.map(row => tools.convertKeys(row)); - - filteredTotal = filteredTotal && filteredTotal[0] && filteredTotal[0].total || 0; - return callback(null, subscriptions, total, filteredTotal); - }); - }); - }); - }); - }; - if (segmentId) { - segments.getQuery(segmentId, (err, queryData) => { + segments.getQuery(segmentId, false, (err, queryData) => { if (err) { return callback(err); } - processQuery(queryData); + + tableHelpers.filter('subscription__' + listId, ['*'], request, columns, ['email', 'first_name', 'last_name'], 'email ASC', queryData, callback); }); } else { - processQuery(false); + tableHelpers.filter('subscription__' + listId, ['*'], request, columns, ['email', 'first_name', 'last_name'], 'email ASC', null, callback); } }; -module.exports.addConfirmation = (listId, email, data, callback) => { - let cid = shortid.generate(); - - tools.validateEmail(email, false, err => { - if (err) { - return callback(err); - } - - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - - let query = 'INSERT INTO confirmations (cid, list, email, data) VALUES (?,?,?,?)'; - connection.query(query, [cid, listId, email, JSON.stringify(data || {})], (err, result) => { - connection.release(); - if (err) { - return callback(err); - } - return callback(null, result && cid || false); - }); - }); - }); -}; - -module.exports.subscribe = (cid, optInIp, callback) => { - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - - let query = 'SELECT * FROM confirmations WHERE cid=? LIMIT 1'; - connection.query(query, [cid], (err, rows) => { - connection.release(); - if (err) { - return callback(err); - } - - if (!rows || !rows.length) { - return callback(null, false); - } - - let subscription; - let listId = rows[0].list; - let email = rows[0].email; - try { - subscription = JSON.parse(rows[0].data); - } catch (E) { - subscription = {}; - } - - subscription.cid = cid; - subscription.list = listId; - subscription.email = email; - - let optInCountry = geoip.lookupCountry(optInIp) || null; - module.exports.insert(listId, { - email, - cid, - optInIp, - optInCountry, - status: 1 - }, subscription, err => { - if (err) { - return callback(err); - } - - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - connection.query('DELETE FROM confirmations WHERE `cid`=? LIMIT 1', [cid], () => { - connection.release(); - callback(null, subscription); - }); - }); - }); - }); - }); -}; - -module.exports.insert = (listId, meta, subscription, callback) => { - +/* + Adds a new subscription. Returns error if a subscription with the same email address is already present and is not unsubscribed. + If it is unsubscribed, the existing subscription is changed based on the provided data. + If meta.partial is true, it updates even an active subscription. + */ +module.exports.insert = (listId, meta, subscriptionData, callback) => { meta = tools.convertKeys(meta); - subscription = tools.convertKeys(subscription); + subscriptionData = tools.convertKeys(subscriptionData); - meta.email = meta.email || subscription.email; + meta.email = meta.email || subscriptionData.email; meta.cid = meta.cid || shortid.generate(); fields.list(listId, (err, fieldList) => { @@ -230,21 +116,29 @@ module.exports.insert = (listId, meta, subscription, callback) => { let keys = []; let values = []; - let allowedKeys = ['first_name', 'last_name']; - Object.keys(subscription).forEach(key => { - let value = subscription[key]; + let allowedKeys = ['first_name', 'last_name', 'tz', 'is_test']; + Object.keys(subscriptionData).forEach(key => { + let value = subscriptionData[key]; key = tools.toDbKey(key); + if (key === 'tz') { + value = (value || '').toString().toLowerCase().trim(); + } + if (key === 'is_test') { + value = value ? '1' : '0'; + } if (allowedKeys.indexOf(key) >= 0) { keys.push(key); values.push(value); } }); - fields.getValues(fields.getRow(fieldList, subscription, true, true), true).forEach(field => { + fields.getValues(fields.getRow(fieldList, subscriptionData, true, true, !!meta.partial), true).forEach(field => { keys.push(field.key); values.push(field.value); }); + values = values.map(v => typeof v === 'string' ? striptags(v) : v); + db.getConnection((err, connection) => { if (err) { return callback(err); @@ -252,16 +146,14 @@ module.exports.insert = (listId, meta, subscription, callback) => { connection.beginTransaction(err => { if (err) { + connection.release(); return callback(err); } - let query = 'SELECT id, status FROM `subscription__' + listId + '` WHERE email=? OR cid=? LIMIT 1'; + let query = 'SELECT `id`, `status`, `cid` FROM `subscription__' + listId + '` WHERE `email`=? OR `cid`=? LIMIT 1'; connection.query(query, [meta.email, meta.cid], (err, rows) => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } let query; @@ -269,15 +161,38 @@ module.exports.insert = (listId, meta, subscription, callback) => { let existing = rows && rows[0] || false; let entryId = existing ? existing.id : false; - meta.status = meta.status || (existing ? existing.status : 1); + meta.cid = existing ? rows[0].cid : meta.cid; + + // meta.status may be 'undefined' or '0' when adding a subscription via API call or CSV import. In both cases meta.partial is 'true'. + // This must either update an existing subscription without changing its status or insert a new subscription with status SUBSCRIBED. + meta.status = meta.status || (existing ? existing.status : Status.SUBSCRIBED); let statusChange = !existing || existing.status !== meta.status; let statusDirection; + if (existing && existing.status === Status.SUBSCRIBED && !meta.partial) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(new Error(_('Email address already registered')))); + } + if (statusChange) { keys.push('status', 'status_change'); values.push(meta.status, new Date()); - statusDirection = !existing ? (meta.status === 1 ? '+' : '-') : (existing.status === 1 ? '-' : '+'); + statusDirection = !existing ? (meta.status === Status.SUBSCRIBED ? '+' : false) : (existing.status === Status.SUBSCRIBED ? '-' : '+'); + } + + if (!keys.length) { + // nothing to update + return connection.commit(err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + connection.release(); + return callback(null, { + entryId, + cid: meta.cid, + inserted: !existing + }); + }); } if (!existing) { @@ -293,32 +208,24 @@ module.exports.insert = (listId, meta, subscription, callback) => { connection.query(query, queryArgs, (err, result) => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } entryId = result.insertId || entryId; - if (statusChange) { + if (statusChange && statusDirection) { connection.query('UPDATE lists SET `subscribers`=`subscribers`' + statusDirection + '1 WHERE id=?', [listId], err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.commit(err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.release(); return callback(null, { entryId, + cid: meta.cid, inserted: !existing }); }); @@ -326,14 +233,12 @@ module.exports.insert = (listId, meta, subscription, callback) => { } else { connection.commit(err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.release(); return callback(null, { entryId, + cid: meta.cid, inserted: !existing }); }); @@ -349,7 +254,7 @@ module.exports.get = (listId, cid, callback) => { cid = (cid || '').toString().trim(); if (!cid) { - return callback(new Error('Missing Subbscription ID')); + return callback(new Error(_('Missing Subscription ID'))); } db.getConnection((err, connection) => { @@ -368,6 +273,66 @@ module.exports.get = (listId, cid, callback) => { } let subscription = tools.convertKeys(rows[0]); + // ensure list id in response + subscription.list = subscription.list || listId; + return callback(null, subscription); + }); + }); +}; + +module.exports.getById = (listId, id, callback) => { + id = Number(id) || 0; + + if (!id) { + return callback(new Error(_('Missing Subscription ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM `subscription__' + listId + '` WHERE id=?', [id], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, false); + } + + let subscription = tools.convertKeys(rows[0]); + // ensure list id in response + subscription.list = subscription.list || listId; + return callback(null, subscription); + }); + }); +}; + +module.exports.getByEmail = (listId, email, callback) => { + if (!email) { + return callback(new Error(_('Missing Subscription email address'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM `subscription__' + listId + '` WHERE email=?', [email], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, false); + } + + let subscription = tools.convertKeys(rows[0]); + // ensure list id in response + subscription.list = subscription.list || listId; return callback(null, subscription); }); }); @@ -392,10 +357,11 @@ module.exports.getWithMergeTags = (listId, cid, callback) => { EMAIL: subscription.email, FIRST_NAME: subscription.firstName, LAST_NAME: subscription.lastName, - FULL_NAME: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' ') + FULL_NAME: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '), + TIMEZONE: subscription.tz || '' }; - fields.getRow(fieldList, subscription, true, true).forEach(field => { + fields.getRow(fieldList, subscription, false, true).forEach(field => { if (field.mergeTag) { subscription.mergeTags[field.mergeTag] = field.mergeValue || ''; } @@ -421,11 +387,11 @@ module.exports.update = (listId, cid, updates, allowEmail, callback) => { let values = []; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } if (!cid) { - return callback(new Error('Missing subscription ID')); + return callback(new Error(_('Missing Subscription ID'))); } fields.list(listId, (err, fieldList) => { @@ -433,7 +399,7 @@ module.exports.update = (listId, cid, updates, allowEmail, callback) => { return callback(err); } - let allowedKeys = ['first_name', 'last_name']; + let allowedKeys = ['first_name', 'last_name', 'tz', 'is_test']; if (allowEmail) { allowedKeys.unshift('email'); @@ -442,13 +408,16 @@ module.exports.update = (listId, cid, updates, allowEmail, callback) => { Object.keys(updates).forEach(key => { let value = updates[key]; key = tools.toDbKey(key); + if (key === 'tz') { + value = (value || '').toString().toLowerCase().trim(); + } if (allowedKeys.indexOf(key) >= 0) { keys.push(key); values.push(value); } }); - fields.getValues(fields.getRow(fieldList, updates, true, true), true).forEach(field => { + fields.getValues(fields.getRow(fieldList, updates, true, true, true), true).forEach(field => { keys.push(field.key); values.push(field.value); }); @@ -457,6 +426,8 @@ module.exports.update = (listId, cid, updates, allowEmail, callback) => { return callback(null, false); } + values = values.map(v => typeof v === 'string' ? striptags(v) : v); + db.getConnection((err, connection) => { if (err) { return callback(err); @@ -474,68 +445,24 @@ module.exports.update = (listId, cid, updates, allowEmail, callback) => { }); }; -module.exports.unsubscribe = (listId, email, campaignId, callback) => { - listId = Number(listId) || 0; - email = (email || '').toString().trim(); - - campaignId = (campaignId || '').toString().trim() || false; - - if (listId < 1) { - return callback(new Error('Missing List ID')); - } - - if (!email) { - return callback(new Error('Missing email address')); - } - - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - - connection.query('SELECT * FROM `subscription__' + listId + '` WHERE `email`=?', [email], (err, rows) => { - connection.release(); - if (err) { - return callback(err); - } - if (!rows || !rows.length || rows[0].status !== 1) { - return callback(null, false); - } - - let subscription = tools.convertKeys(rows[0]); - module.exports.changeStatus(subscription.id, listId, campaignId, 2, err => { - if (err) { - return callback(err); - } - return callback(null, subscription); - }); - }); - }); -}; - -module.exports.changeStatus = (id, listId, campaignId, status, callback) => { +module.exports.changeStatus = (listId, id, campaignId, status, callback) => { db.getConnection((err, connection) => { if (err) { return callback(err); } connection.beginTransaction(err => { if (err) { + connection.release(); return callback(err); } connection.query('SELECT `status` FROM `subscription__' + listId + '` WHERE id=? LIMIT 1', [id], (err, rows) => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } if (!rows || !rows.length) { - return connection.rollback(() => { - connection.release(); - return callback(null, false); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(null, false)); } let oldStatus = rows[0].status; @@ -543,31 +470,22 @@ module.exports.changeStatus = (id, listId, campaignId, status, callback) => { let statusDirection; if (!statusChange) { - return connection.rollback(() => { - connection.release(); - return callback(null, true); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(null, true)); } - if (statusChange && oldStatus === 1 || status === 1) { - statusDirection = status === 1 ? '+' : '-'; + if (statusChange && oldStatus === Status.SUBSCRIBED || status === Status.SUBSCRIBED) { + statusDirection = status === Status.SUBSCRIBED ? '+' : '-'; } connection.query('UPDATE `subscription__' + listId + '` SET `status`=?, `status_change`=NOW() WHERE id=? LIMIT 1', [status, id], err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } if (!statusDirection) { return connection.commit(err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.release(); return callback(null, true); @@ -576,41 +494,61 @@ module.exports.changeStatus = (id, listId, campaignId, status, callback) => { connection.query('UPDATE `lists` SET `subscribers`=`subscribers`' + statusDirection + '1 WHERE id=? LIMIT 1', [listId], err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } - if (!campaignId) { + // status change is not related to a campaign or it marks message as bounced etc. + if (!campaignId || status !== Status.SUBSCRIBED && status !== Status.UNSUBSCRIBED) { return connection.commit(err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.release(); return callback(null, true); }); } - connection.query('UPDATE `campaigns` SET `unsubscribed`=`unsubscribed`+1 WHERE `cid`=? LIMIT 1', [campaignId], err => { + connection.query('SELECT `id` FROM `campaigns` WHERE `cid`=? LIMIT 1', [campaignId], (err, rows) => { if (err) { - return connection.rollback(() => { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + let campaign = rows && rows[0] || false; + + if (!campaign) { + // should not happend + return connection.commit(err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } connection.release(); - return callback(err); + return callback(null, true); }); } - return connection.commit(err => { + + // we should see only unsubscribe events here but you never know + connection.query('UPDATE `campaigns` SET `unsubscribed`=`unsubscribed`' + (status === Status.UNSUBSCRIBED ? '+' : '-') + '1 WHERE `cid`=? LIMIT 1', [campaignId], err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } - connection.release(); - return callback(null, true); + + let query = 'UPDATE `campaign__' + campaign.id + '` SET `status`=? WHERE `list`=? AND `subscription`=? LIMIT 1'; + let values = [status, listId, id]; + + // Updated tracker status + connection.query(query, values, err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + return connection.commit(err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + connection.release(); + return callback(null, true); + }); + }); }); }); }); @@ -625,11 +563,11 @@ module.exports.delete = (listId, cid, callback) => { cid = (cid || '').toString().trim(); if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } if (!cid) { - return callback(new Error('Missing subscription ID')); + return callback(new Error(_('Missing subscription ID'))); } db.getConnection((err, connection) => { @@ -651,24 +589,19 @@ module.exports.delete = (listId, cid, callback) => { connection.beginTransaction(err => { if (err) { + connection.release(); return callback(err); } connection.query('DELETE FROM `subscription__' + listId + '` WHERE cid=? LIMIT 1', [cid], err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } - if (subscription.status !== 1) { + if (subscription.status !== Status.SUBSCRIBED) { return connection.commit(err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.release(); return callback(null, subscription.email); @@ -677,17 +610,11 @@ module.exports.delete = (listId, cid, callback) => { connection.query('UPDATE lists SET subscribers=subscribers-1 WHERE id=? LIMIT 1', [listId], err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.commit(err => { if (err) { - return connection.rollback(() => { - connection.release(); - return callback(err); - }); + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); } connection.release(); return callback(null, subscription.email); @@ -699,9 +626,9 @@ module.exports.delete = (listId, cid, callback) => { }); }; -module.exports.createImport = (listId, type, path, size, delimiter, mapping, callback) => { +module.exports.createImport = (listId, type, path, size, delimiter, emailcheck, mapping, callback) => { listId = Number(listId) || 0; - type = Number(type) || 1; + type = Number(type) || 0; if (listId < 1) { return callback(new Error('Missing List ID')); @@ -711,8 +638,8 @@ module.exports.createImport = (listId, type, path, size, delimiter, mapping, cal if (err) { return callback(err); } - let query = 'INSERT INTO importer (`list`, `type`, `path`, `size`, `delimiter`, `mapping`) VALUES(?,?,?,?,?,?)'; - connection.query(query, [listId, type, path, size, delimiter, JSON.stringify(mapping)], (err, result) => { + let query = 'INSERT INTO importer (`list`, `type`, `path`, `size`, `delimiter`, `emailcheck`, `mapping`) VALUES(?,?,?,?,?,?,?)'; + connection.query(query, [listId, type, path, size, delimiter, emailcheck, JSON.stringify(mapping)], (err, result) => { connection.release(); if (err) { return callback(err); @@ -727,11 +654,11 @@ module.exports.updateImport = (listId, importId, data, callback) => { importId = Number(importId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } if (importId < 1) { - return callback(new Error('Missing Import ID')); + return callback(new Error(_('Missing Import ID'))); } let keys = []; @@ -767,11 +694,10 @@ module.exports.updateImport = (listId, importId, data, callback) => { connection.release(); return callback(null, affected); }); - return; + } else { + connection.release(); + return callback(null, affected); } - - connection.release(); - return callback(null, affected); }); }); }; @@ -781,11 +707,11 @@ module.exports.getImport = (listId, importId, callback) => { importId = Number(importId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } if (importId < 1) { - return callback(new Error('Missing Import ID')); + return callback(new Error(_('Missing Import ID'))); } db.getConnection((err, connection) => { @@ -821,7 +747,7 @@ module.exports.getFailedImports = (importId, callback) => { importId = Number(importId) || 0; if (importId < 1) { - return callback(new Error('Missing Import ID')); + return callback(new Error(_('Missing Import ID'))); } db.getConnection((err, connection) => { @@ -844,7 +770,7 @@ module.exports.listImports = (listId, callback) => { listId = Number(listId) || 0; if (listId < 1) { - return callback(new Error('Missing List ID')); + return callback(new Error(_('Missing List ID'))); } db.getConnection((err, connection) => { @@ -878,3 +804,130 @@ module.exports.listImports = (listId, callback) => { }); }); }; + +/* +Performs checks before update of an address. This includes finding the existing subscriber, validating the new email +and checking whether the new email does not conflict with other subscribers. + */ +module.exports.updateAddressCheck = (list, cid, emailNew, ip, callback) => { + cid = (cid || '').toString().trim(); + + if (!list || !list.id) { + return callback(new Error(_('Missing List ID'))); + } + + if (!cid) { + return callback(new Error(_('Missing subscription ID'))); + } + + tools.validateEmail(emailNew, false, err => { + if (err) { + return callback(err); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'SELECT * FROM `subscription__' + list.id + '` WHERE `cid`=? AND `status`=' + Status.SUBSCRIBED + ' LIMIT 1'; + let args = [cid]; + connection.query(query, args, (err, rows) => { + if (err) { + connection.release(); + return callback(err); + } + if (!rows || !rows.length) { + connection.release(); + return callback(new Error(_('Unknown subscription ID'))); + } + + if (rows[0].email === emailNew) { + connection.release(); + return callback(new Error(_('Nothing seems to be changed'))); + } + + let old = rows[0]; + + let query = 'SELECT `id` FROM `subscription__' + list.id + '` WHERE `email`=? AND `cid`<>? AND `status`=' + Status.SUBSCRIBED + ' LIMIT 1'; + let args = [emailNew, cid]; + connection.query(query, args, (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (rows && rows.length > 0) { + return callback(null, old, false); + } else { + return callback(null, old, true); + } + }); + }); + }); + }); +}; + + +/* + Updates address in subscription__xxx + */ +module.exports.updateAddress = (listId, subscriptionId, emailNew, callback) => { + // update email address instead of adding new + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + connection.beginTransaction(err => { + if (err) { + connection.release(); + return callback(err); + } + + let query = 'SELECT `id` FROM `subscription__' + listId + '` WHERE `email`=? AND `id`<>? AND `status`=' + Status.SUBSCRIBED + ' LIMIT 1'; + let args = [emailNew, subscriptionId]; + connection.query(query, args, (err, rows) => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + if (rows && rows.length > 0) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(new Error(_('Email address already registered')))); + } + + let query = 'DELETE FROM `subscription__' + listId + '` WHERE `email`=? AND `id`<>?'; + let args = [emailNew, subscriptionId]; + connection.query(query, args, err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + let query = 'UPDATE `subscription__' + listId + '` SET `email`=? WHERE `id`=? AND `status`=' + Status.SUBSCRIBED + ' LIMIT 1'; + let args = [emailNew, subscriptionId]; + connection.query(query, args, (err, result) => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + + if (!result || !result.affectedRows) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(new Error(_('Subscription not found in this list')))); + } + + return connection.commit(err => { + if (err) { + return helpers.rollbackAndReleaseConnection(connection, () => callback(err)); + } + connection.release(); + + return callback(); + }); + }); + }); + }); + }); + }); + +}; + +module.exports.getUnsubscriptionMode = (list, subscriptionId) => list.unsubscriptionMode; // eslint-disable-line no-unused-vars +// TODO: Once the unsubscription mode is customizable per segment, then this will be a good place to process it. diff --git a/lib/models/templates.js b/lib/models/templates.js index ea54cac3..b0f0099e 100644 --- a/lib/models/templates.js +++ b/lib/models/templates.js @@ -2,52 +2,28 @@ let db = require('../db'); let tools = require('../tools'); +let _ = require('../translate')._; +let tableHelpers = require('../table-helpers'); -let allowedKeys = ['description', 'html', 'text']; +let allowedKeys = ['description', 'editor_name', 'editor_data', 'html', 'text']; module.exports.list = (start, limit, callback) => { - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } + tableHelpers.list('templates', ['*'], 'name', null, start, limit, callback); +}; - connection.query('SELECT SQL_CALC_FOUND_ROWS * FROM templates ORDER BY name LIMIT ? OFFSET ?', [limit, start], (err, rows) => { - if (err) { - connection.release(); - return callback(err); - } - connection.query('SELECT FOUND_ROWS() AS total', (err, total) => { - connection.release(); - if (err) { - return callback(err); - } - return callback(null, rows, total && total[0] && total[0].total); - }); - }); - }); +module.exports.filter = (request, parent, callback) => { + tableHelpers.filter('templates', ['*'], request, ['#', 'name', 'description'], ['name'], 'name ASC', null, callback); }; module.exports.quicklist = callback => { - db.getConnection((err, connection) => { - if (err) { - return callback(err); - } - - connection.query('SELECT id, name FROM templates ORDER BY name LIMIT 1000', (err, rows) => { - connection.release(); - if (err) { - return callback(err); - } - return callback(null, (rows || []).map(tools.convertKeys)); - }); - }); + tableHelpers.quicklist('templates', ['id', 'name'], 'name', callback); }; module.exports.get = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Template ID')); + return callback(new Error(_('Missing Template ID'))); } db.getConnection((err, connection) => { @@ -76,7 +52,7 @@ module.exports.create = (template, callback) => { let data = tools.convertKeys(template); if (!(data.name || '').toString().trim()) { - return callback(new Error('Template Name must be set')); + return callback(new Error(_('Template Name must be set'))); } let name = (template.name || '').toString().trim(); @@ -85,12 +61,17 @@ module.exports.create = (template, callback) => { let values = [name]; Object.keys(template).forEach(key => { - let value = template[key].trim(); + let value = template[key]; key = tools.toDbKey(key); - if (allowedKeys.indexOf(key) >= 0) { - keys.push(key); - values.push(value); + if (!allowedKeys.includes(key)) { + return; } + value = value.trim(); + if (key === 'description') { + value = tools.purifyHTML(value); + } + keys.push(key); + values.push(value); }); db.getConnection((err, connection) => { @@ -118,11 +99,11 @@ module.exports.update = (id, updates, callback) => { let data = tools.convertKeys(updates); if (id < 1) { - return callback(new Error('Missing Template ID')); + return callback(new Error(_('Missing Template ID'))); } if (!(data.name || '').toString().trim()) { - return callback(new Error('Template Name must be set')); + return callback(new Error(_('Template Name must be set'))); } let name = (updates.name || '').toString().trim(); @@ -132,6 +113,9 @@ module.exports.update = (id, updates, callback) => { Object.keys(updates).forEach(key => { let value = updates[key].trim(); key = tools.toDbKey(key); + if (key === 'description') { + value = tools.purifyHTML(value); + } if (allowedKeys.indexOf(key) >= 0) { keys.push(key); values.push(value); @@ -155,11 +139,22 @@ module.exports.update = (id, updates, callback) => { }); }; +module.exports.duplicate = (id, callback) => module.exports.get(id, (err, template) => { + if (err) { + return callback(err); + } + if (!template) { + return callback(new Error(_('Template does not exist'))); + } + template.name = template.name + ' Copy'; + return module.exports.create(template, callback); +}); + module.exports.delete = (id, callback) => { id = Number(id) || 0; if (id < 1) { - return callback(new Error('Missing Template ID')); + return callback(new Error(_('Missing Template ID'))); } db.getConnection((err, connection) => { diff --git a/lib/models/triggers.js b/lib/models/triggers.js new file mode 100644 index 00000000..b87125c4 --- /dev/null +++ b/lib/models/triggers.js @@ -0,0 +1,427 @@ +'use strict'; + +let log = require('npmlog'); +let tools = require('../tools'); +let db = require('../db'); +let lists = require('./lists'); +let segments = require('./segments'); +let util = require('util'); +let _ = require('../translate')._; +let tableHelpers = require('../table-helpers'); + +module.exports.defaultColumns = [{ + column: 'created', + name: _('Sign up date'), + type: 'date' +}, { + column: 'latest_open', + name: _('Latest open'), + type: 'date' +}, { + column: 'latest_click', + name: _('Latest click'), + type: 'date' +}]; + +module.exports.defaultCampaignEvents = [{ + option: 'delivered', + name: _('Delivered') +}, { + option: 'opened', + name: _('Has Opened') +}, { + option: 'clicked', + name: _('Has Clicked') +}, { + option: 'not_opened', + name: _('Not Opened') +}, { + option: 'not_clicked', + name: _('Not Clicked') +}]; + +let defaultColumnMap = {}; +let defaultEventMap = {}; +module.exports.defaultColumns.forEach(col => defaultColumnMap[col.column] = col.name); +module.exports.defaultCampaignEvents.forEach(evt => defaultEventMap[evt.option] = evt.name); + +module.exports.list = callback => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let tableFields = [ + '`triggers`.`id` AS `id`', + '`triggers`.`name` AS `name`', + '`triggers`.`description` AS `description`', + '`triggers`.`enabled` AS `enabled`', + '`triggers`.`list` AS `list`', + '`triggers`.`segment` AS `segment`', + '`lists`.`name` AS `list_name`', + '`segments`.`name` AS `segment_name`', + '`source`.`id` AS `source_campaign`', + '`source`.`name` AS `source_campaign_name`', + '`dest`.`id` AS `dest_campaign`', + '`dest`.`name` AS `dest_campaign_name`', + '`triggers`.`count` AS `count`', + '`custom_fields`.`id` AS `column_id`', + '`triggers`.`column` AS `column`', + '`custom_fields`.`name` AS `column_name`', + '`triggers`.`rule` AS `rule`', + '`triggers`.`seconds` AS `seconds`', + '`triggers`.`created` AS `created`' + ]; + + let query = 'SELECT ' + tableFields.join(', ') + ' FROM `triggers` LEFT JOIN `campaigns` `source` ON `source`.`id`=`triggers`.`source_campaign` LEFT JOIN `campaigns` `dest` ON `dest`.`id`=`triggers`.`dest_campaign` LEFT JOIN `lists` ON `lists`.`id`=`triggers`.`list` LEFT JOIN `segments` ON `segments`.`id`=`triggers`.`segment` LEFT JOIN `custom_fields` ON `custom_fields`.`list` = `triggers`.`list` AND `custom_fields`.`column`=`triggers`.`column` ORDER BY `triggers`.`name`'; + connection.query(query, (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + let triggers = (rows || []).map(tools.convertKeys).map(row => { + if (row.rule === 'subscription' && row.column && !row.columnName) { + row.columnName = defaultColumnMap[row.column]; + } + + let days = Math.round(row.seconds / (24 * 3600)); + row.formatted = util.format('%s days after %s', days, row.rule === 'subscription' ? row.columnName : (util.format('%s %s', defaultEventMap[row.column], row.sourceCampaign, row.sourceCampaignName))); + + return row; + }); + return callback(null, triggers); + }); + }); +}; + +module.exports.getQuery = (id, callback) => { + module.exports.get(id, (err, trigger) => { + if (err) { + return callback(err); + } + + let limit = 300; + + // 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'; + + + let getSegmentQuery = (segmentId, next) => { + segmentId = Number(segmentId); + if (!segmentId) { + return next(null, { + where: '', + values: [] + }); + } + + segments.getQuery(segmentId, 'subscription', next); + }; + + getSegmentQuery(trigger.segment, (err, queryData) => { + if (err) { + log.err('Triggers', err); + return null; + } + + let query = false; + let querySegmentSubscription = ''; + let querySegmentTriggertable = ''; + if (trigger.segment > 0) + { + querySegmentSubscription = (queryData.where ? ' AND (' + queryData.where + ')' : ''); + querySegmentTriggertable = ' AND triggertable.`segment` = ' + trigger.segment; + } + + switch (trigger.rule) { + case 'subscription': + query = 'SELECT id FROM `subscription__' + trigger.list + '` subscription WHERE status=1 AND ' + intervalQuery('`' + trigger.column + '`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit; + break; + case 'campaign': + switch (trigger.column) { + case 'delivered': + query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit; + break; + case 'not_clicked': + query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=0 WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND tracker.created IS NULL ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit; + break; + case 'not_opened': + query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=-1 WHERE campaign.status=1 AND ' + intervalQuery('`campaign`.`created`', trigger.seconds, treshold) + ' AND tracker.created IS NULL ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit; + break; + case 'clicked': + query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=0 WHERE campaign.status=1 AND ' + intervalQuery('`tracker`.`created`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit; + break; + case 'opened': + query = 'SELECT subscription.id AS id FROM `subscription__' + trigger.list + '` subscription LEFT JOIN `campaign__' + trigger.sourceCampaign + '` campaign ON campaign.list=' + trigger.list + ' AND subscription.id=campaign.subscription LEFT JOIN `campaign_tracker__' + trigger.sourceCampaign + '` tracker ON tracker.list=campaign.list AND tracker.subscriber=subscription.id AND tracker.link=-1 WHERE campaign.status=1 AND ' + intervalQuery('`tracker`.`created`', trigger.seconds, treshold) + ' ' + querySegmentSubscription + ' AND subscription.id NOT IN (SELECT subscription FROM `trigger__' + id + '` triggertable WHERE triggertable.`list` = ' + trigger.list + ' ' + querySegmentTriggertable + ' AND triggertable.`subscription` = subscription.`id`) LIMIT ' + limit; + break; + } + break; + } + + if (trigger.segment > 0) { + let values = queryData.values.concat([trigger.list, trigger.segment]); + for (let i = 0; i < values.length; i++) { + query = query.replace('?', db.escape(values[i])); + } + } + + callback(null, query); + + }); + + }); +}; + +module.exports.get = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error('Missing Trigger ID')); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT * FROM triggers WHERE id=?', [id], (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + + if (!rows || !rows.length) { + return callback(null, false); + } + + let trigger = tools.convertKeys(rows[0]); + return callback(null, trigger); + }); + }); +}; + +module.exports.create = (trigger, callback) => { + + trigger = tools.convertKeys(trigger); + let name = (trigger.name || '').toString().trim(); + let description = (trigger.description || '').toString().trim(); + let listId = Number(trigger.list) || 0; + let segmentId = Number(trigger.segmentId) || 0; + let seconds = (Number(trigger.days) || 0) * 24 * 3600; + let rule = (trigger.rule || '').toString().toLowerCase().trim(); + let destCampaign = Number(trigger.destCampaign) || 0; + let sourceCampaign = null; + let column; + + if (!listId) { + return callback(new Error(_('Missing or invalid list ID'))); + } + + if (seconds < 0) { + return callback(new Error(_('Days in the past are not allowed'))); + } + + if (!rule || ['campaign', 'subscription'].indexOf(rule) < 0) { + return callback(new Error(_('Missing or invalid trigger rule'))); + } + + switch (rule) { + case 'subscription': + column = (trigger.column || '').toString().toLowerCase().trim(); + if (!column) { + return callback(new Error(_('Invalid subscription configuration'))); + } + break; + case 'campaign': + column = (trigger.campaignOption || '').toString().toLowerCase().trim(); + sourceCampaign = Number(trigger.sourceCampaign) || 0; + if (!column || !sourceCampaign) { + return callback(new Error(_('Invalid campaign configuration'))); + } + if (sourceCampaign === destCampaign) { + return callback(new Error(_('A campaing can not be a target for itself'))); + } + break; + default: + return callback(new Error(_('Missing or invalid trigger rule'))); + } + + lists.get(listId, (err, list) => { + if (err) { + return callback(err); + } + if (!list) { + return callback(new Error(_('Missing or invalid list ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let keys = ['name', 'description', 'list', 'segment', 'source_campaign', 'rule', 'column', 'seconds', 'dest_campaign', 'last_check']; + let values = [name, description, list.id, segmentId, sourceCampaign, rule, column, seconds, destCampaign]; + + let query = 'INSERT INTO `triggers` (`' + keys.join('`, `') + '`) VALUES (' + values.map(() => '?').join(', ') + ', NOW())'; + + connection.query(query, values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + let id = result && result.insertId; + if (!id) { + return callback(new Error(_('Could not store trigger row'))); + } + + createTriggerTable(id, err => { + if (err) { + return callback(err); + } + return callback(null, id); + }); + }); + }); + }); +}; + +module.exports.update = (id, trigger, callback) => { + id = Number(id) || 0; + if (id < 1) { + return callback(new Error(_('Missing or invalid Trigger ID'))); + } + + trigger = tools.convertKeys(trigger); + let name = (trigger.name || '').toString().trim(); + let description = (trigger.description || '').toString().trim(); + let enabled = trigger.enabled ? 1 : 0; + let seconds = (Number(trigger.days) || 0) * 24 * 3600; + let rule = (trigger.rule || '').toString().toLowerCase().trim(); + let destCampaign = Number(trigger.destCampaign) || 0; + let sourceCampaign = null; + let column; + + if (seconds < 0) { + return callback(new Error(_('Days in the past are not allowed'))); + } + + if (!rule || ['campaign', 'subscription'].indexOf(rule) < 0) { + return callback(new Error(_('Missing or invalid trigger rule'))); + } + + switch (rule) { + case 'subscription': + column = (trigger.column || '').toString().toLowerCase().trim(); + if (!column) { + return callback(new Error(_('Invalid subscription configuration'))); + } + break; + case 'campaign': + column = (trigger.campaignOption || '').toString().toLowerCase().trim(); + sourceCampaign = Number(trigger.sourceCampaign) || 0; + if (!column || !sourceCampaign) { + return callback(new Error(_('Invalid campaign configuration'))); + } + if (sourceCampaign === destCampaign) { + return callback(new Error(_('A campaing can not be a target for itself'))); + } + break; + default: + return callback(new Error(_('Missing or invalid trigger rule'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let keys = ['name', 'description', 'enabled', 'source_campaign', 'rule', 'column', 'seconds', 'dest_campaign']; + let values = [name, description, enabled, sourceCampaign, rule, column, seconds, destCampaign]; + + let query = 'UPDATE `triggers` SET ' + keys.map(key => '`' + key + '`=?').join(', ') + ' WHERE `id`=? LIMIT 1'; + + connection.query(query, values.concat(id), (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + return callback(null, result && result.affectedRows); + }); + }); +}; + +module.exports.delete = (id, callback) => { + id = Number(id) || 0; + + if (id < 1) { + return callback(new Error(_('Missing Trigger ID'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('DELETE FROM triggers WHERE id=? LIMIT 1', [id], (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + + let affected = result && result.affectedRows || 0; + removeTriggerTable(id, err => { + if (err) { + return callback(err); + } + return callback(null, affected); + }); + }); + }); +}; + +module.exports.filterSubscribers = (trigger, request, columns, callback) => { + let queryData = { + where: 'trigger__' + trigger.id + '.list=?', + values: [trigger.list] + }; + + tableHelpers.filter('subscription__' + trigger.list + ' JOIN trigger__' + trigger.id + ' ON trigger__' + trigger.id + '.subscription=subscription__' + trigger.list + '.id', ['*'], request, columns, ['email', 'first_name', 'last_name'], 'email ASC', queryData, callback); +}; + +function createTriggerTable(id, callback) { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + let query = 'CREATE TABLE `trigger__' + id + '` LIKE `trigger`'; + connection.query(query, err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + }); +} + +function removeTriggerTable(id, callback) { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + let query = 'DROP TABLE IF EXISTS `trigger__' + id + '`'; + connection.query(query, err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + }); +} diff --git a/lib/models/users.js b/lib/models/users.js index 6631d292..b16106c9 100644 --- a/lib/models/users.js +++ b/lib/models/users.js @@ -9,6 +9,7 @@ let mailer = require('../mailer'); let settings = require('./settings'); let crypto = require('crypto'); let urllib = require('url'); +let _ = require('../translate')._; /** * Fetches user by ID value @@ -21,7 +22,7 @@ module.exports.get = (id, callback) => { if (err) { return callback(err); } - connection.query('SELECT id, username, email FROM users WHERE id=? LIMIT 1', [id], (err, rows) => { + connection.query('SELECT `id`, `username`, `email`, `access_token` FROM `users` WHERE `id`=? LIMIT 1', [id], (err, rows) => { connection.release(); if (err) { @@ -38,6 +39,75 @@ module.exports.get = (id, callback) => { }); }; +module.exports.findByAccessToken = (accessToken, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT `id`, `username`, `email`, `access_token` FROM `users` WHERE `access_token`=? LIMIT 1', [accessToken], (err, rows) => { + connection.release(); + + if (err) { + return callback(err); + } + + if (!rows.length) { + return callback(null, false); + } + + let user = tools.convertKeys(rows[0]); + return callback(null, user); + }); + }); +}; + +module.exports.findByUsername = (username, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT `id`, `username`, `email`, `access_token` FROM `users` WHERE `username`=? LIMIT 1', [username], (err, rows) => { + connection.release(); + + if (err) { + return callback(err); + } + + if (!rows.length) { + return callback(null, false); + } + + let user = tools.convertKeys(rows[0]); + return callback(null, user); + }); + }); +}; + +module.exports.add = (username, password, email, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('INSERT INTO `users` (`username`, `password`, `email`, `created`) VALUES (?, ?, ?, NOW())', [username, password, email], (err, result) => { + connection.release(); + + if (err) { + return callback(err); + } + + let id = result && result.insertId; + if (!id) { + return callback(new Error(_('Could not store user row'))); + } + + return callback(null, id); + }); + }); +}; + /** * Fetches user by username and password * @@ -47,8 +117,12 @@ module.exports.get = (id, callback) => { */ module.exports.authenticate = (username, password, callback) => { + if (password === '') { + return callback(null, false); + } + let login = (connection, callback) => { - connection.query('SELECT id, password FROM users WHERE username=? OR email=? LIMIT 1', [username, username], (err, rows) => { + connection.query('SELECT `id`, `password`, `access_token` FROM `users` WHERE `username`=? OR email=? LIMIT 1', [username, username], (err, rows) => { if (err) { return callback(err); } @@ -64,8 +138,10 @@ module.exports.authenticate = (username, password, callback) => { if (!result) { return callback(null, false); } + + let user = tools.convertKeys(rows[0]); return callback(null, { - id: rows[0].id, + id: user.id, username }); }); @@ -94,7 +170,7 @@ module.exports.authenticate = (username, password, callback) => { module.exports.update = (id, updates, callback) => { if (!updates.email) { - return callback(new Error('Email Address must be set')); + return callback(new Error(_('Email Address must be set'))); } let update = (connection, callback) => { @@ -105,7 +181,7 @@ module.exports.update = (id, updates, callback) => { } if (!rows.length) { - return callback('Failed to check user data'); + return callback(_('Failed to check user data')); } let keys = ['email']; @@ -116,7 +192,7 @@ module.exports.update = (id, updates, callback) => { connection.query('UPDATE users SET ' + keys.map(key => key + '=?').join(', ') + ' WHERE id=? LIMIT 1', values, (err, result) => { if (err) { if (err.code === 'ER_DUP_ENTRY') { - err = new Error('Can\'t change email as another user with the same email address already exists'); + err = new Error(_('Can\'t change email as another user with the same email address already exists')); } return callback(err); } @@ -133,15 +209,15 @@ module.exports.update = (id, updates, callback) => { return callback(err); } if (!result) { - return callback('Incorrect current password'); + return callback(_('Incorrect current password')); } if (!updates.password) { - return callback(new Error('New password not set')); + return callback(new Error(_('New password not set'))); } if (updates.password !== updates.password2) { - return callback(new Error('Passwords do not match')); + return callback(new Error(_('Passwords do not match'))); } bcrypt.hash(updates.password, null, null, (err, hash) => { @@ -175,11 +251,39 @@ module.exports.update = (id, updates, callback) => { }); }; +module.exports.resetToken = (id, callback) => { + id = Number(id) || 0; + + if (!id) { + return callback(new Error(_('User ID not set'))); + } + + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let token = crypto.randomBytes(20).toString('hex').toLowerCase(); + let query = 'UPDATE users SET `access_token`=? WHERE id=? LIMIT 1'; + let values = [token, id]; + + connection.query(query, values, (err, result) => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, result.affectedRows); + }); + }); + +}; + + module.exports.sendReset = (username, callback) => { username = (username || '').toString().trim(); if (!username) { - return callback(new Error('Username must be set')); + return callback(new Error(_('Username must be set'))); } db.getConnection((err, connection) => { @@ -216,7 +320,7 @@ module.exports.sendReset = (username, callback) => { to: { address: rows[0].email }, - subject: 'Mailer password change request' + subject: _('Mailer password change request') }, { html: 'emails/password-reset-html.hbs', text: 'emails/password-reset-text.hbs', @@ -227,7 +331,7 @@ module.exports.sendReset = (username, callback) => { } }, err => { if (err) { - log.error('Mail', err.stack); // eslint-disable-line no-console + log.error('Mail', err); // eslint-disable-line no-console } }); @@ -240,7 +344,7 @@ module.exports.sendReset = (username, callback) => { module.exports.checkResetToken = (username, resetToken, callback) => { if (!username || !resetToken) { - return callback(new Error('Missing username or reset token')); + return callback(new Error(_('Missing username or reset token'))); } db.getConnection((err, connection) => { if (err) { @@ -260,11 +364,11 @@ module.exports.resetPassword = (data, callback) => { let updates = tools.convertKeys(data); if (!updates.username || !updates.resetToken) { - return callback(new Error('Missing username or reset token')); + return callback(new Error(_('Missing username or reset token'))); } if (!updates.password || !updates.password2 || updates.password !== updates.password2) { - return callback(new Error('Invalid new password')); + return callback(new Error(_('Invalid new password'))); } bcrypt.hash(updates.password, null, null, (err, hash) => { diff --git a/lib/passport.js b/lib/passport.js index 111f24a1..364d34fe 100644 --- a/lib/passport.js +++ b/lib/passport.js @@ -1,12 +1,36 @@ 'use strict'; let config = require('config'); +let log = require('npmlog'); +let _ = require('./translate')._; +let util = require('util'); + let passport = require('passport'); +let fs = require('fs'); let LocalStrategy = require('passport-local').Strategy; + let csrf = require('csurf'); let bodyParser = require('body-parser'); let users = require('./models/users'); +let LdapStrategy; +try { + LdapStrategy = require('passport-ldapjs').Strategy; // eslint-disable-line global-require +} catch (E) { + if (config.ldap.enabled) { + log.info('LDAP', 'Module "passport-ldapjs" not installed. It will not be used for LDAP auth.'); + } +} + +let LdapAuthStrategy; +try { + LdapAuthStrategy = require('passport-ldapauth').Strategy; // eslint-disable-line global-require +} catch (E) { + if (config.ldapauth.enabled) { + log.info('LDAP', 'Module "passport-ldapauth" not installed. It will not be used for LDAP auth.'); + } +} + module.exports.csrfProtection = csrf({ cookie: true }); @@ -23,20 +47,22 @@ module.exports.setup = app => { module.exports.logout = (req, res) => { if (req.user) { - req.flash('info', req.user.username + ' logged out'); + req.flash('info', util.format(_('%s logged out'), req.user.username)); req.logout(); } res.redirect('/'); }; module.exports.login = (req, res, next) => { - passport.authenticate('local', (err, user, info) => { + let authMode = config.ldapauth.enabled ? 'ldapauth' : config.ldap.enabled ? 'ldap' : 'local'; + passport.authenticate(authMode, (err, user, info) => { if (err) { req.flash('danger', err.message); return next(err); } if (!user) { - req.flash('danger', info && info.message || 'Failed to authenticate user'); + log.warn('auth', `[client ${req.ip}] authentication failure`); + req.flash('danger', info && info.message || _('Failed to authenticate user')); return res.redirect('/users/login' + (req.body.next ? '?next=' + encodeURIComponent(req.body.next) : '')); } req.logIn(user, err => { @@ -52,27 +78,124 @@ module.exports.login = (req, res, next) => { req.session.cookie.expires = false; } - req.flash('success', 'Logged in as ' + user.username); + req.flash('success', util.format(_('Logged in as %s'), user.username)); return res.redirect(req.body.next || '/'); }); })(req, res, next); }; -passport.use(new LocalStrategy((username, password, done) => { - users.authenticate(username, password, (err, user) => { - if (err) { - return done(err); - } +if (config.ldap.enabled && LdapStrategy) { + log.info('Using LDAP auth (passport-ldapjs)'); - if (!user) { - return done(null, false, { - message: 'Incorrect username or password' - }); - } + let opts = { + server: { + url: config.ldap.url, + tlsOptions: { + ca: config.ldap.ca ? [ + fs.readFileSync(config.ldap.ca) + ] : undefined + } + }, + base: config.ldap.baseDN, + search: { + filter: config.ldap.filter, + attributes: [config.ldap.uidTag, 'mail'], + scope: 'sub' + }, + uidTag: config.ldap.uidTag, + bindUser: config.ldap.bindUser, + bindPassword: config.ldap.bindPassword + }; - return done(null, user); - }); -})); + passport.use(new LdapStrategy(opts, (profile, done) => { + users.findByUsername(profile[config.ldap.uidTag], (err, user) => { + if (err) { + return done(err); + } + + if (!user) { + // password is empty for ldap + users.add(profile[config.ldap.uidTag], '', profile.mail, (err, id) => { + if (err) { + return done(err); + } + + return done(null, { + id, + username: profile[config.ldap.uidTag] + }); + }); + } else { + return done(null, { + id: user.id, + username: user.username + }); + } + }); + })); +} else if (config.ldapauth.enabled && LdapAuthStrategy) { + log.info('Using LDAP auth (passport-ldapauth)'); + let opts = { + server: { + url: config.ldapauth.url, + searchBase: config.ldapauth.baseDN, + searchFilter: config.ldapauth.filter, + searchAttributes: [config.ldapauth.uidTag, 'mail'], + bindDN: config.ldapauth.bindUser, + bindCredentials: config.ldapauth.bindPassword, + tlsOptions: { + ca: config.ldapauth.ca ? [ + fs.readFileSync(config.ldapauth.ca) + ] : undefined + } + } + }; + + passport.use(new LdapAuthStrategy(opts, (profile, done) => { + users.findByUsername(profile[config.ldapauth.uidTag], (err, user) => { + if (err) { + return done(err); + } + + if (!user) { + // password is empty for ldap + users.add(profile[config.ldapauth.uidTag], '', profile.mail, (err, id) => { + if (err) { + return done(err); + } + + return done(null, { + id, + username: profile[config.ldapauth.uidTag] + }); + }); + } else { + return done(null, { + id: user.id, + username: user.username + }); + } + }); + })); +} else { + log.info('Using local auth'); + + passport.use(new LocalStrategy((username, password, done) => { + users.authenticate(username, password, (err, user) => { + if (err) { + return done(err); + } + + if (!user) { + return done(null, false, { + message: _('Incorrect username or password') + }); + } + + return done(null, user); + }); + })); +} passport.serializeUser((user, done) => { done(null, user.id); diff --git a/lib/privilege-helpers.js b/lib/privilege-helpers.js new file mode 100644 index 00000000..07d39844 --- /dev/null +++ b/lib/privilege-helpers.js @@ -0,0 +1,77 @@ +'use strict'; + +const log = require('npmlog'); +const config = require('config'); + +const fs = require('fs'); + +const tryRequire = require('try-require'); +const posix = tryRequire('posix'); + +function _getConfigUidGid(prefix, defaultUid, defaultGid) { + let uid = defaultUid; + let gid = defaultGid; + + if (posix) { + try { + if (config.user) { + uid = posix.getpwnam(config[prefix + 'user']).uid; + } + } catch (err) { + log.info('PrivilegeHelpers', 'Failed to resolve user id "%s"', config[prefix + 'user']); + } + + try { + if (config.user) { + gid = posix.getpwnam(config[prefix + 'group']).gid; + } + } catch (err) { + log.info('PrivilegeHelpers', 'Failed to resolve group id "%s"', config[prefix + 'group']); + } + } else { + log.info('PrivilegeHelpers', 'Posix module not installed. Cannot resolve uid/gid'); + } + + return { uid, gid }; +} + +function getConfigUidGid() { + return _getConfigUidGid('', process.getuid(), process.getgid()); +} + +function getConfigROUidGid() { + let rwIds = getConfigUidGid(); + return _getConfigUidGid('ro', rwIds.uid, rwIds.gid); +} + +function ensureMailtrainOwner(file, callback) { + const ids = getConfigUidGid(); + fs.chown(file, ids.uid, ids.gid, callback); +} + +function dropRootPrivileges() { + if (config.group) { + try { + process.setgid(config.group); + log.info('PrivilegeHelpers', 'Changed group to "%s" (%s)', config.group, process.getgid()); + } catch (E) { + log.info('PrivilegeHelpers', 'Failed to change group to "%s" (%s)', config.group, E.message); + } + } + + if (config.user) { + try { + process.setuid(config.user); + log.info('PrivilegeHelpers', 'Changed user to "%s" (%s)', config.user, process.getuid()); + } catch (E) { + log.info('PrivilegeHelpers', 'Failed to change user to "%s" (%s)', config.user, E.message); + } + } +} + +module.exports = { + dropRootPrivileges, + ensureMailtrainOwner, + getConfigUidGid, + getConfigROUidGid +}; diff --git a/lib/report-processor.js b/lib/report-processor.js new file mode 100644 index 00000000..82653849 --- /dev/null +++ b/lib/report-processor.js @@ -0,0 +1,147 @@ +'use strict'; + +const log = require('npmlog'); +const reports = require('./models/reports'); +const executor = require('./executor'); + +let runningWorkersCount = 0; +let maxWorkersCount = 1; + +let workers = {}; + +function startWorker(report) { + + function onStarted(tid) { + log.info('ReportProcessor', 'Worker process for "%s" started with tid %s. Current worker count is %s.', report.name, tid, runningWorkersCount); + workers[report.id] = tid; + } + + function onFinished(code, signal) { + runningWorkersCount--; + log.info('ReportProcessor', 'Worker process for "%s" (tid %s) exited with code %s signal %s. Current worker count is %s.', report.name, workers[report.id], code, signal, runningWorkersCount); + delete workers[report.id]; + + const fields = {}; + if (code === 0) { + fields.state = reports.ReportState.FINISHED; + fields.lastRun = new Date(); + } else { + fields.state = reports.ReportState.FAILED; + } + + reports.updateFields(report.id, fields, err => { + if (err) { + log.error('ReportProcessor', err); + } + + setImmediate(startWorkers); + }); + } + + function onFailed(msg) { + runningWorkersCount--; + log.error('ReportProcessor', 'Executing worker process for "%s" (tid %s) failed with message "%s". Current worker count is %s.', report.name, workers[report.id], msg, runningWorkersCount); + delete workers[report.id]; + + const fields = { + state: reports.ReportState.FAILED + }; + + reports.updateFields(report.id, fields, err => { + if (err) { + log.error('ReportProcessor', err); + } + + setImmediate(startWorkers); + }); + } + + const reportData = { + id: report.id, + name: report.name + }; + + runningWorkersCount++; + executor.start('report-processor-worker', reportData, onStarted, onFinished, onFailed); +} + +function startWorkers() { + reports.listWithState(reports.ReportState.SCHEDULED, 0, maxWorkersCount - runningWorkersCount, (err, reportList) => { + if (err) { + log.error('ReportProcessor', err); + return; + } + + for (let report of reportList) { + reports.updateFields(report.id, { state: reports.ReportState.PROCESSING }, err => { + if (err) { + log.error('ReportProcessor', err); + return; + } + + startWorker(report); + }); + } + }); +} + +module.exports.start = (reportId, callback) => { + if (!workers[reportId]) { + log.info('ReportProcessor', 'Scheduling report id: %s', reportId); + reports.updateFields(reportId, { state: reports.ReportState.SCHEDULED, lastRun: null}, err => { + if (err) { + return callback(err); + } + + if (runningWorkersCount < maxWorkersCount) { + log.info('ReportProcessor', 'Starting worker because runningWorkersCount=%s maxWorkersCount=%s', runningWorkersCount, maxWorkersCount); + + startWorkers(); + } else { + log.info('ReportProcessor', 'Not starting worker because runningWorkersCount=%s maxWorkersCount=%s', runningWorkersCount, maxWorkersCount); + } + + callback(null); + }); + } else { + log.info('ReportProcessor', 'Worker for report id: %s is already running.', reportId); + } +}; + +module.exports.stop = (reportId, callback) => { + const tid = workers[reportId]; + if (tid) { + log.info('ReportProcessor', 'Killing worker for report id: %s', reportId); + executor.stop(tid); + reports.updateFields(reportId, { state: reports.ReportState.FAILED}, callback); + } else { + log.info('ReportProcessor', 'No running worker found for report id: %s', reportId); + } +}; + +module.exports.init = callback => { + reports.listWithState(reports.ReportState.PROCESSING, 0, 0, (err, reportList) => { + if (err) { + log.error('ReportProcessor', err); + } + + function scheduleReport() { + if (reportList.length > 0) { + const report = reportList.shift(); + + reports.updateFields(report.id, { state: reports.ReportState.SCHEDULED}, err => { + if (err) { + log.error('ReportProcessor', err); + } + + scheduleReport(); + }); + } + + startWorkers(); + return callback(); + } + + scheduleReport(); + }); +}; diff --git a/lib/subscription-mail-helpers.js b/lib/subscription-mail-helpers.js new file mode 100644 index 00000000..71ed13af --- /dev/null +++ b/lib/subscription-mail-helpers.js @@ -0,0 +1,155 @@ +'use strict'; + +const log = require('npmlog'); +let fields = require('./models/fields'); +let settings = require('./models/settings'); +let mailer = require('./mailer'); +let urllib = require('url'); +let helpers = require('./helpers'); +let _ = require('./translate')._; +let util = require('util'); + + +module.exports = { + sendAlreadySubscribed, + sendConfirmAddressChange, + sendConfirmSubscription, + sendConfirmUnsubscription, + sendSubscriptionConfirmed, + sendUnsubscriptionConfirmed +}; + +function sendSubscriptionConfirmed(list, email, subscription, callback) { + const relativeUrls = { + preferencesUrl: '/subscription/' + list.cid + '/manage/' + subscription.cid, + unsubscribeUrl: '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid + }; + + sendMail(list, email, 'subscription-confirmed', _('%s: Subscription Confirmed'), relativeUrls, {}, subscription, callback); +} + +function sendAlreadySubscribed(list, email, subscription, callback) { + const mailOpts = { + ignoreDisableConfirmations: true + }; + const relativeUrls = { + preferencesUrl: '/subscription/' + list.cid + '/manage/' + subscription.cid, + unsubscribeUrl: '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid + }; + sendMail(list, email, 'already-subscribed', _('%s: Email Address Already Registered'), relativeUrls, mailOpts, subscription, callback); +} + +function sendConfirmAddressChange(list, email, cid, subscription, callback) { + const mailOpts = { + ignoreDisableConfirmations: true + }; + const relativeUrls = { + confirmUrl: '/subscription/confirm/change-address/' + cid + }; + sendMail(list, email, 'confirm-address-change', _('%s: Please Confirm Email Change in Subscription'), relativeUrls, mailOpts, subscription, callback); +} + +function sendConfirmSubscription(list, email, cid, subscription, callback) { + const mailOpts = { + ignoreDisableConfirmations: true + }; + const relativeUrls = { + confirmUrl: '/subscription/confirm/subscribe/' + cid + }; + sendMail(list, email, 'confirm-subscription', _('%s: Please Confirm Subscription'), relativeUrls, mailOpts, subscription, callback); +} + +function sendConfirmUnsubscription(list, email, cid, subscription, callback) { + const mailOpts = { + ignoreDisableConfirmations: true + }; + const relativeUrls = { + confirmUrl: '/subscription/confirm/unsubscribe/' + cid + }; + sendMail(list, email, 'confirm-unsubscription', _('%s: Please Confirm Unsubscription'), relativeUrls, mailOpts, subscription, callback); +} + +function sendUnsubscriptionConfirmed(list, email, subscription, callback) { + const relativeUrls = { + subscribeUrl: '/subscription/' + list.cid + '?cid=' + subscription.cid + }; + sendMail(list, email, 'unsubscription-confirmed', _('%s: Unsubscription Confirmed'), relativeUrls, {}, subscription, callback); +} + + +function sendMail(list, email, template, subject, relativeUrls, mailOpts, subscription, callback) { + fields.list(list.id, (err, fieldList) => { + if (err) { + return callback(err); + } + + let encryptionKeys = []; + fields.getRow(fieldList, subscription).forEach(field => { + if (field.type === 'gpg' && field.value) { + encryptionKeys.push(field.value.trim()); + } + }); + + settings.list(['defaultHomepage', 'defaultFrom', 'defaultAddress', 'defaultPostaddress', 'serviceUrl', 'disableConfirmations'], (err, configItems) => { + if (err) { + return callback(err); + } + + if (!mailOpts.ignoreDisableConfirmations && configItems.disableConfirmations) { + return callback(); + } + + const data = { + title: list.name, + homepage: configItems.defaultHomepage || configItems.serviceUrl, + contactAddress: configItems.defaultAddress, + defaultPostaddress: configItems.defaultPostaddress + }; + + for (let relativeUrlKey in relativeUrls) { + data[relativeUrlKey] = urllib.resolve(configItems.serviceUrl, relativeUrls[relativeUrlKey]); + } + + let text = { + template: 'subscription/mail-' + template + '-text.hbs' + }; + + let html = { + template: 'subscription/mail-' + template + '-html.mjml.hbs', + layout: 'subscription/layout.mjml.hbs', + type: 'mjml' + }; + + helpers.injectCustomFormTemplates(list.defaultForm, { text, html }, (err, tmpl) => { + if (!err && tmpl) { + text = tmpl.text || text; + html = tmpl.html || html; + } + + mailer.sendMail({ + from: { + name: configItems.defaultFrom, + address: configItems.defaultAddress + }, + to: { + name: [].concat(subscription.firstName || []).concat(subscription.lastName || []).join(' '), + address: email + }, + subject: util.format(subject, list.name), + encryptionKeys + }, { + html, + text, + data + }, err => { + if (err) { + log.error('Subscription', err); + } + }); + + callback(); + + }); + }); + }); +} diff --git a/lib/table-helpers.js b/lib/table-helpers.js new file mode 100644 index 00000000..1176eba2 --- /dev/null +++ b/lib/table-helpers.js @@ -0,0 +1,134 @@ +'use strict'; + +const db = require('./db'); +const tools = require('./tools'); + +module.exports.list = (source, fields, orderBy, queryData, start, limit, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let limitQuery = ''; + let limitValues = []; + if (limit) { + limitQuery = ' LIMIT ?'; + limitValues.push(limit); + + if (start) { + limitQuery += ' OFFSET ?'; + limitValues.push(start); + } + } + + let whereClause = ''; + let whereValues = []; + + if (queryData) { + whereClause = ' WHERE ' + queryData.where; + whereValues = queryData.values; + } + + connection.query('SELECT SQL_CALC_FOUND_ROWS ' + fields.join(', ') + ' FROM ' + source + whereClause + ' ORDER BY ' + orderBy + ' DESC' + limitQuery, whereValues.concat(limitValues), (err, rows) => { + if (err) { + connection.release(); + return callback(err); + } + connection.query('SELECT FOUND_ROWS() AS total', (err, total) => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, rows, total && total[0] && total[0].total); + }); + }); + }); +}; + +module.exports.quicklist = (source, fields, orderBy, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT ' + fields.join(', ') + ' FROM ' + source + ' ORDER BY ' + orderBy + ' LIMIT 1000', (err, rows) => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, (rows || []).map(tools.convertKeys)); + }); + }); +}; + +module.exports.filter = (source, fields, request, columns, searchFields, defaultOrdering, queryData, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'SELECT COUNT(*) AS total FROM ' + source; + let values = []; + + if (queryData) { + query += ' WHERE ' + queryData.where; + values = values.concat(queryData.values || []); + } + + connection.query(query, values, (err, total) => { + if (err) { + connection.release(); + return callback(err); + } + total = total && total[0] && total[0].total || 0; + + let ordering = []; + + if (request.order && request.order.length) { + + request.order.forEach(order => { + let orderField = columns[Number(order.column)]; + let orderDirection = (order.dir || '').toString().toLowerCase() === 'desc' ? 'DESC' : 'ASC'; + if (orderField) { + ordering.push(orderField + ' ' + orderDirection); + } + }); + } + + if (!ordering.length) { + ordering.push(defaultOrdering); + } + + let searchWhere = ''; + let searchArgs = []; + + if (request.search && request.search.value) { + let searchVal = '%' + request.search.value.replace(/\\/g, '\\\\').replace(/([%_])/g, '\\$1') + '%'; + + searchWhere = searchFields.map(field => field + ' LIKE ?').join(' OR '); + searchArgs = searchFields.map(() => searchVal); + } + + let query = 'SELECT SQL_CALC_FOUND_ROWS ' + fields.join(', ') + ' FROM ' + source +' WHERE ' + (searchWhere ? '(' + searchWhere + ')': '1') + (queryData ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?'; + let args = searchArgs.concat(queryData ? queryData.values : []).concat([Number(request.length) || 50, Number(request.start) || 0]); + + connection.query(query, args, (err, rows) => { + if (err) { + connection.release(); + return callback(err); + } + connection.query('SELECT FOUND_ROWS() AS total', (err, filteredTotal) => { + connection.release(); + if (err) { + return callback(err); + } + + rows = rows.map(row => tools.convertKeys(row)); + + filteredTotal = filteredTotal && filteredTotal[0] && filteredTotal[0].total || 0; + return callback(null, rows, total, filteredTotal); + }); + }); + }); + }); +}; diff --git a/lib/tools.js b/lib/tools.js index 6a8a98c8..b0d16822 100644 --- a/lib/tools.js +++ b/lib/tools.js @@ -1,9 +1,19 @@ 'use strict'; +const config = require('config'); +let fs = require('fs'); +let path = require('path'); let db = require('./db'); let slugify = require('slugify'); let Isemail = require('isemail'); let urllib = require('url'); +let juice = require('juice'); +let jsdom = require('jsdom'); +let he = require('he'); +let _ = require('./translate')._; +let util = require('util'); +let createDOMPurify = require('dompurify'); +let htmlToText = require('html-to-text'); let blockedUsers = ['abuse', 'admin', 'billing', 'compliance', 'devnull', 'dns', 'ftp', 'hostmaster', 'inoc', 'ispfeedback', 'ispsupport', 'listrequest', 'list', 'maildaemon', 'noc', 'noreply', 'noreply', 'null', 'phish', 'phishing', 'postmaster', 'privacy', 'registrar', 'root', 'security', 'spam', 'support', 'sysadmin', 'tech', 'undisclosedrecipients', 'unsubscribe', 'usenet', 'uucp', 'webmaster', 'www']; @@ -16,21 +26,31 @@ module.exports = { updateMenu, validateEmail, formatMessage, - getMessageLinks + getMessageLinks, + prepareHtml, + purifyHTML, + mergeTemplateIntoLayout, + workers: new Set() }; function toDbKey(key) { return key. - replace(/[^a-z0-9\-_]/gi, ''). - replace(/\-+/g, '_'). - replace(/[A-Z]+/g, c => '_' + c.toLowerCase()). - replace(/^_+|_+$/g, ''). - replace(/_+/g, '_'). - trim(); + replace(/[^a-z0-9\-_]/gi, ''). + replace(/-+/g, '_'). + replace(/[A-Z]/g, c => '_' + c.toLowerCase()). + replace(/^_+|_+$/g, ''). + replace(/_+/g, '_'). + trim(); } function fromDbKey(key) { - return key.replace(/[_\-]([a-z])/g, (m, c) => c.toUpperCase()); + let prefix = ''; + if (key.startsWith('_')) { + key = key.substring(1); + prefix = '_'; + + } + return prefix + key.replace(/[_-]([a-z])/g, (m, c) => c.toUpperCase()); } function convertKeys(obj, options) { @@ -41,7 +61,7 @@ function convertKeys(obj, options) { if (options.skip && options.skip.indexOf(lKey) >= 0) { return; } - if (options.keep && options.skip.indexOf(lKey) < 0) { + if (options.keep && options.keep.indexOf(lKey) < 0) { return; } response[lKey] = obj[key]; @@ -51,9 +71,9 @@ function convertKeys(obj, options) { function queryParams(obj) { return Object.keys(obj). - filter(key => key !== '_csrf'). - map(key => encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])). - join('&'); + filter(key => key !== '_csrf'). + map(key => encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])). + join('&'); } function createSlug(table, name, callback) { @@ -102,25 +122,36 @@ function updateMenu(res) { } res.locals.menu.push({ - title: 'Lists', + title: _('Lists'), url: '/lists', key: 'lists' }, { - title: 'Templates', + title: _('Templates'), url: '/templates', key: 'templates' }, { - title: 'Campaigns', + title: _('Campaigns'), url: '/campaigns', key: 'campaigns' + }, { + title: _('Automation'), + url: '/triggers', + key: 'triggers' }); + + if (config.reports && config.reports.enabled === true) { + res.locals.menu.push({ + title: _('Reports'), + url: '/reports', + key: 'reports' + }); + } } function validateEmail(address, checkBlocked, callback) { - let user = (address || '').toString().split('@').shift().toLowerCase().replace(/[^a-z0-9]/g, ''); if (checkBlocked && blockedUsers.indexOf(user) >= 0) { - return callback(new Error('Blocked email address "' + address + '"')); + return callback(new Error(util.format(_('Blocked email address "%s"'), address))); } Isemail.validate(address, { @@ -129,16 +160,16 @@ function validateEmail(address, checkBlocked, callback) { }, result => { if (result !== 0) { - let message = 'Invalid email address "' + address + '"'; + let message = util.format(_('Invalid email address "%s".'), address); switch (result) { case 5: - message += '. MX record not found for domain'; + message += ' ' + _('MX record not found for domain'); break; case 6: - message += '. Address domain not found'; + message += ' ' + _('Address domain not found'); break; case 12: - message += '. Address domain name is required'; + message += ' ' + _('Address domain name is required'); break; } return callback(new Error(message)); @@ -150,31 +181,143 @@ function validateEmail(address, checkBlocked, callback) { function getMessageLinks(serviceUrl, campaign, list, subscription) { return { - LINK_UNSUBSCRIBE: urllib.resolve(serviceUrl, '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid + '?auto=yes&c=' + campaign.cid), + LINK_UNSUBSCRIBE: campaign.unsubscribe ? campaign.unsubscribe : urllib.resolve(serviceUrl, '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid + '?c=' + campaign.cid), LINK_PREFERENCES: urllib.resolve(serviceUrl, '/subscription/' + list.cid + '/manage/' + subscription.cid), - LINK_BROWSER: urllib.resolve(serviceUrl, '/archive/' + campaign.cid + '/' + list.cid + '/' + subscription.cid) + LINK_BROWSER: urllib.resolve(serviceUrl, '/archive/' + campaign.cid + '/' + list.cid + '/' + subscription.cid), + CAMPAIGN_ID: campaign.cid, + LIST_ID: list.cid, + SUBSCRIPTION_ID: subscription.cid }; } -function formatMessage(serviceUrl, campaign, list, subscription, message, filter) { +function formatMessage(serviceUrl, campaign, list, subscription, message, filter, isHTML) { filter = typeof filter === 'function' ? filter : (str => str); let links = getMessageLinks(serviceUrl, campaign, list, subscription); - let getValue = key => { + let getTagValue = key => { key = (key || '').toString().toUpperCase().trim(); if (links.hasOwnProperty(key)) { return links[key]; } if (subscription.mergeTags.hasOwnProperty(key)) { - return subscription.mergeTags[key]; + let value = (subscription.mergeTags[key] || '').toString(); + let containsHTML = /<[a-z][\s\S]*>/.test(value); + return isHTML ? he.encode((containsHTML ? value : value.replace(/(?:\r\n|\r|\n)/g, '
')), { + useNamedReferences: true, + allowUnsafeSymbols: true + }) : (containsHTML ? htmlToText.fromString(value) : value); } return false; }; - return message.replace(/\[([a-z0-9_]+)(?:\/([^\]]+))?\]/ig, (match, identifier, fallback) => { - identifier = identifier.toUpperCase(); - let value = (getValue(identifier) || fallback || '').trim(); - return value ? filter(value) : match; + let tagReplace = message => + message.replace(/\[([a-z0-9_]+)(?:\/([^\]]+))?\]/ig, (match, identifier, fallback) => { + identifier = identifier.toUpperCase(); + let value = getTagValue(identifier); + if (value === false) { + return match; + } + value = (value || fallback || '').trim(); + return filter(value); + }); + + return tagReplace(tagReplace(message)); +} + +function prepareHtml(html, callback) { + if (!(html || '').toString().trim()) { + return callback(null, false); + } + jsdom.env(false, false, { + html, + features: { + FetchExternalResources: false, // disables resource loading over HTTP / filesystem + ProcessExternalResources: false // do not execute JS within script blocks + } + }, (err, win) => { + if (err) { + return callback(err); + } + + let head = win.document.querySelector('head'); + let hasCharsetTag = false; + let metaTags = win.document.querySelectorAll('meta'); + if (metaTags) { + for (let i = 0; i < metaTags.length; i++) { + if (metaTags[i].hasAttribute('charset')) { + metaTags[i].setAttribute('charset', 'utf-8'); + hasCharsetTag = true; + break; + } + } + } + if (!hasCharsetTag) { + let charsetTag = win.document.createElement('meta'); + charsetTag.setAttribute('charset', 'utf-8'); + head.appendChild(charsetTag); + } + let preparedHtml = '' + win.document.documentElement.innerHTML + ''; + + return callback(null, juice(preparedHtml)); }); } + +function purifyHTML(html) { + let win = jsdom.jsdom('', { + features: { + FetchExternalResources: false, // disables resource loading over HTTP / filesystem + ProcessExternalResources: false // do not execute JS within script blocks + } + }).defaultView; + let DOMPurify = createDOMPurify(win); + return DOMPurify.sanitize(html); +} + +// TODO Simplify! +function mergeTemplateIntoLayout(template, layout, callback) { + + layout = layout || '{{{body}}}'; + + let readFile = (relPath, callback) => { + fs.readFile(path.join(__dirname, '..', 'views', relPath), 'utf-8', (err, source) => { + if (err) { + return callback(err); + } + callback(null, source); + }); + }; + + let done = (template, layout) => { + let source = layout.replace(/\{\{\{body\}\}\}/g, template); + return callback(null, source); + }; + + if (layout.endsWith('.hbs')) { + readFile(layout, (err, layout) => { + if (err) { + return callback(err); + } + // Please dont end your custom messages with .hbs ... + if (template.endsWith('.hbs')) { + readFile(template, (err, template) => { + if (err) { + return callback(err); + } + return done(template, layout); + }); + } else { + return done(template, layout); + } + }); + } else if (template.endsWith('.hbs')) { + readFile(template, (err, template) => { + if (err) { + return callback(err); + } + return done(template, layout); + }); + } else { + return done(template, layout); + } +} diff --git a/lib/translate.js b/lib/translate.js new file mode 100644 index 00000000..9abd9a6f --- /dev/null +++ b/lib/translate.js @@ -0,0 +1,41 @@ +'use strict'; + +const config = require('config'); + +const Gettext = require('node-gettext'); +const gt = new Gettext(); +const fs = require('fs'); +const path = require('path'); +const log = require('npmlog'); +const gettextParser = require('gettext-parser'); +const fakelang = require('./fakelang'); + +const language = config.language || 'en'; + +[].concat(config.language || []).forEach(lang => { + let data; + let file = path.join(__dirname, '..', 'languages', lang + '.mo'); + try { + data = gettextParser.mo.parse(fs.readFileSync(file)); + } catch (E) { + // ignore + } + if (data) { + gt.addTranslations(lang, lang, data); + gt.setTextDomain(lang); + gt.setLocale(lang); + log.info('LANG', 'Loaded language file for %s', lang); + } +}); + +module.exports._ = str => { + if (typeof str !== 'string') { + str = String(str); + } + + if (language === 'zz') { + return fakelang(str); + } + + return gt.dgettext(language, str); +}; diff --git a/meta.json b/meta.json index b4a9b4a7..f48d12cd 100644 --- a/meta.json +++ b/meta.json @@ -1,3 +1,3 @@ { - "schemaVersion": 4 + "schemaVersion": 34 } diff --git a/nitrous-post-create.sh b/nitrous-post-create.sh deleted file mode 100755 index 47841ed5..00000000 --- a/nitrous-post-create.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -export DEBIAN_FRONTEND=noninteractive - -sudo apt-add-respository ppa:chris-lea/redis-server - -sudo apt-get update -sudo -E apt-get -q -y install mysql-server pwgen redis-server -sudo apt-get clean - -MYSQL_PASSWORD=`pwgen -1` - -mysql -u root -e "CREATE USER 'mailtrain'@'localhost' IDENTIFIED BY '$MYSQL_PASSWORD';" -mysql -u root -e "GRANT ALL PRIVILEGES ON mailtrain.* TO 'mailtrain'@'%' WITH GRANT OPTION;" -mysql -u mailtrain --password="$MYSQL_PASSWORD" -e "CREATE database mailtrain;" - -cat >> config/production.toml < setup/sql/mailtrain.sql", - "sqldrop": "node setup/sql/drop.js" + "sqldump": "node setup/sql/dump.js | sed -e '/^\\/\\*.*\\*\\/;$/d' -e 's/.[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]./NOW()/g' > setup/sql/mailtrain${DUMP_NAME_SUFFIX}.sql", + "sqldrop": "node setup/sql/drop.js", + "sqlgen": "npm run sqldrop && DB_FROM_START=Y npm run sqlinit && npm run sqldump", + "langs:hbs": "jsxgettext -L handlebars -k translate -o langs/hbs.pot views/layout.hbs views/index.hbs", + "langs:js": "jsxgettext -o languages/js.pot routes/index.js", + "langs": "npm run langs:hbs && npm run langs:js", + "sqldumptest": "NODE_ENV=test DUMP_NAME_SUFFIX=-test npm run sqldump", + "sqlresettest": "NODE_ENV=test npm run sqldrop && NODE_ENV=test npm run sqlinit", + "starttest": "NODE_ENV=test node index.js", + "_e2e": "NODE_ENV=test node test/e2e/index.js", + "e2e": "npm run sqlresettest && npm run _e2e" }, "repository": { "type": "git", - "url": "git://github.com/andris9/mailtrain.git" + "url": "git://github.com/Mailtrain-org/mailtrain.git" }, "author": "Andris Reinman", "license": "GPL-3.0", - "homepage": "http://mailtrain.org", + "homepage": "https://mailtrain.org/", "engines": { "node": ">=5.0.0" }, "devDependencies": { + "babel-eslint": "^8.0.0", + "bluebird": "^3.5.0", + "chai": "^4.1.2", + "eslint-config-nodemailer": "^1.2.0", "grunt": "^1.0.1", "grunt-cli": "^1.2.0", "grunt-contrib-nodeunit": "^1.0.0", - "grunt-eslint": "^18.1.0" + "grunt-eslint": "^20.1.0", + "jsxgettext-andris": "^0.9.0-patch.1", + "lodash": "^4.17.4", + "mocha": "^3.5.3", + "phantomjs-prebuilt": "^2.1.15", + "selenium-webdriver": "^3.5.0", + "url-pattern": "^1.0.3" + }, + "optionalDependencies": { + "posix": "^4.1.1" }, "dependencies": { + "async": "^2.5.0", + "aws-sdk": "^2.122.0", "bcrypt-nodejs": "0.0.3", - "body-parser": "^1.15.0", - "bounce-handler": "^7.3.2-fork.0", - "compression": "^1.6.1", - "config": "^1.20.1", + "body-parser": "^1.18.2", + "bounce-handler": "^7.3.2-fork.2", + "compression": "^1.7.0", + "config": "^1.26.2", "connect-flash": "^0.1.1", - "connect-redis": "^3.0.2", - "cookie-parser": "^1.4.1", - "csurf": "^1.8.3", - "csv-parse": "^1.0.4", + "connect-redis": "^3.3.0", + "cookie-parser": "^1.4.3", + "cors": "^2.8.4", + "csurf": "^1.9.0", + "csv-parse": "^1.2.3", + "device": "^0.3.8", + "dompurify": "^1.0.2", "escape-html": "^1.0.3", - "express": "^4.13.4", - "express-session": "^1.13.0", - "geoip-ultralight": "^0.1.3", - "handlebars": "^4.0.5", - "hbs": "^4.0.0", - "html-to-text": "^2.1.0", + "escape-string-regexp": "^1.0.5", + "express": "^4.15.5", + "express-session": "^1.15.5", + "faker": "^4.1.0", + "feedparser": "^2.2.1", + "fs-extra": "^4.0.2", + "geoip-ultralight": "^0.1.5", + "gettext-parser": "^1.3.0", + "gm": "^1.23.0", + "handlebars": "^4.0.10", + "hbs": "^4.0.1", + "he": "^1.1.1", + "html-to-text": "^3.3.0", "humanize": "0.0.9", - "isemail": "^2.1.0", - "morgan": "^1.7.0", - "multer": "^1.1.0", - "mysql": "^2.10.2", - "nodemailer": "^2.3.2", - "nodemailer-openpgp": "^1.0.2", - "npmlog": "^2.0.3", - "openpgp": "^2.2.2", - "passport": "^0.3.2", + "is-url": "^1.2.2", + "isemail": "^2.2.1", + "jquery-file-upload-middleware": "^0.1.8", + "jsdom": "^9.12.0", + "json-stringify-date": "^0.1.4", + "juice": "^4.1.1", + "libmime": "^3.1.0", + "mailparser": "^2.0.5", + "marked": "^0.3.6", + "memory-cache": "^0.2.0", + "mjml": "3.3.5", + "mkdirp": "^0.5.1", + "moment-timezone": "^0.5.13", + "morgan": "^1.8.2", + "multer": "^1.3.0", + "multiparty": "^4.1.3", + "mysql": "^2.14.1", + "node-gettext": "^2.0.0", + "node-mocks-http": "^1.6.5", + "nodemailer": "^4.1.1", + "nodemailer-openpgp": "^1.1.0", + "npmlog": "^4.1.2", + "object-hash": "^1.1.8", + "openpgp": "^2.5.11", + "passport": "^0.4.0", "passport-local": "^1.0.0", - "request": "^2.72.0", - "serve-favicon": "^2.3.0", - "shortid": "^2.2.6", - "slugify": "^0.1.1", - "smtp-server": "^1.9.0", - "striptags": "^2.1.1", - "toml": "^2.3.0" + "passport-ldapauth": "^2.0.0", + "premailer-api": "^1.0.4", + "redfour": "^1.0.2", + "redis": "^2.8.0", + "request": "^2.82.0", + "request-promise": "^4.2.2", + "serve-favicon": "^2.4.4", + "shortid": "^2.2.8", + "slugify": "^1.2.1", + "smtp-server": "^3.1.0", + "striptags": "^3.1.0", + "toml": "^2.3.3", + "try-require": "^1.2.1" } } diff --git a/protected/reports/.gitignore b/protected/reports/.gitignore new file mode 100644 index 00000000..d5b76284 --- /dev/null +++ b/protected/reports/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!README.md \ No newline at end of file diff --git a/protected/reports/README.md b/protected/reports/README.md new file mode 100644 index 00000000..0420f4d0 --- /dev/null +++ b/protected/reports/README.md @@ -0,0 +1 @@ +This directory serves for generated reports. \ No newline at end of file diff --git a/public/ace/mode-css.js b/public/ace/mode-css.js new file mode 100644 index 00000000..84cd16c6 --- /dev/null +++ b/public/ace/mode-css.js @@ -0,0 +1 @@ +ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}) \ No newline at end of file diff --git a/public/ace/mode-handlebars.js b/public/ace/mode-handlebars.js new file mode 100644 index 00000000..bd345d55 --- /dev/null +++ b/public/ace/mode-handlebars.js @@ -0,0 +1 @@ +ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),ace.define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({noJSX:!0})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),ace.define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getCursorPosition(),a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name"))f=a.stepBackward();var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[A-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),ace.define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),ace.define("ace/mode/handlebars_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/html_highlight_rules"],function(e,t,n){"use strict";function s(e,t){return t.splice(0,3),t.shift()||"start"}var r=e("../lib/oop"),i=e("./html_highlight_rules").HtmlHighlightRules,o=function(){i.call(this);var e={regex:"(?={{)",push:"handlebars"};for(var t in this.$rules)this.$rules[t].unshift(e);this.$rules.handlebars=[{token:"comment.start",regex:"{{!--",push:[{token:"comment.end",regex:"--}}",next:s},{defaultToken:"comment"}]},{token:"comment.start",regex:"{{!",push:[{token:"comment.end",regex:"}}",next:s},{defaultToken:"comment"}]},{token:"support.function",regex:"{{{",push:[{token:"support.function",regex:"}}}",next:s},{token:"variable.parameter",regex:"[a-zA-Z_$][a-zA-Z0-9_$]*"}]},{token:"storage.type.start",regex:"{{[#\\^/&]?",push:[{token:"storage.type.end",regex:"}}",next:s},{token:"variable.parameter",regex:"[a-zA-Z_$][a-zA-Z0-9_$]*"}]}],this.normalizeRules()};r.inherits(o,i),t.HandlebarsHighlightRules=o}),ace.define("ace/mode/behaviour/html",["require","exports","module","ace/lib/oop","ace/mode/behaviour/xml"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour/xml").XmlBehaviour,s=function(){i.call(this)};r.inherits(s,i),t.HtmlBehaviour=s}),ace.define("ace/mode/handlebars",["require","exports","module","ace/lib/oop","ace/mode/html","ace/mode/handlebars_highlight_rules","ace/mode/behaviour/html","ace/mode/folding/html"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html").Mode,s=e("./handlebars_highlight_rules").HandlebarsHighlightRules,o=e("./behaviour/html").HtmlBehaviour,u=e("./folding/html").FoldMode,a=function(){i.call(this),this.HighlightRules=s,this.$behaviour=new o,this.foldingRules=new u};r.inherits(a,i),function(){this.blockComment={start:"{{!--",end:"--}}"},this.$id="ace/mode/handlebars"}.call(a.prototype),t.Mode=a}) \ No newline at end of file diff --git a/public/ace/mode-javascript.js b/public/ace/mode-javascript.js new file mode 100644 index 00000000..26a8bdc8 --- /dev/null +++ b/public/ace/mode-javascript.js @@ -0,0 +1 @@ +ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}) \ No newline at end of file diff --git a/public/ace/mode-json.js b/public/ace/mode-json.js new file mode 100644 index 00000000..f6226b80 --- /dev/null +++ b/public/ace/mode-json.js @@ -0,0 +1 @@ +ace.define("ace/mode/json_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"variable",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)'},{token:"string",regex:'"',next:"string"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:"invalid.illegal",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"invalid.illegal",regex:"\\/\\/.*$"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],string:[{token:"constant.language.escape",regex:/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\\/bfnrt])/},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:'"',next:"start"},{token:"string",regex:"",next:"start"}]}};r.inherits(s,i),t.JsonHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/json",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./json_highlight_rules").JsonHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./behaviour/cstyle").CstyleBehaviour,a=e("./folding/cstyle").FoldMode,f=e("../worker/worker_client").WorkerClient,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new u,this.foldingRules=new a};r.inherits(l,i),function(){this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new f(["ace"],"ace/mode/json_worker","JsonWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/json"}.call(l.prototype),t.Mode=l}) \ No newline at end of file diff --git a/public/ace/mode-plain_text.js b/public/ace/mode-plain_text.js new file mode 100644 index 00000000..21cf21da --- /dev/null +++ b/public/ace/mode-plain_text.js @@ -0,0 +1 @@ +ace.define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,u=function(){this.HighlightRules=s,this.$behaviour=new o};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return""},this.$id="ace/mode/plain_text"}.call(u.prototype),t.Mode=u}) diff --git a/public/ace/worker-css.js b/public/ace/worker-css.js new file mode 100644 index 00000000..408c6bd0 --- /dev/null +++ b/public/ace/worker-css.js @@ -0,0 +1 @@ +"no use strict";(function(e){function t(e,t){var n=e,r="";while(n){var i=t[n];if(typeof i=="string")return i+r;if(i)return i.location.replace(/\/*$/,"/")+(r||i.main||i.name);if(i===!1)return"";var s=n.lastIndexOf("/");if(s===-1)break;r=n.substr(s)+r,n=n.slice(0,s)}return e}if(typeof e.window!="undefined"&&e.document)return;if(e.require&&e.define)return;e.console||(e.console=function(){var e=Array.prototype.slice.call(arguments,0);postMessage({type:"log",data:e})},e.console.error=e.console.warn=e.console.log=e.console.trace=e.console),e.window=e,e.ace=e,e.onerror=function(e,t,n,r,i){postMessage({type:"error",data:{message:e,data:i.data,file:t,line:n,col:r,stack:i.stack}})},e.normalizeModule=function(t,n){if(n.indexOf("!")!==-1){var r=n.split("!");return e.normalizeModule(t,r[0])+"!"+e.normalizeModule(t,r[1])}if(n.charAt(0)=="."){var i=t.split("/").slice(0,-1).join("/");n=(i?i+"/":"")+n;while(n.indexOf(".")!==-1&&s!=n){var s=n;n=n.replace(/^\.\//,"").replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return n},e.require=function(r,i){i||(i=r,r=null);if(!i.charAt)throw new Error("worker.js require() accepts only (parentId, id) as arguments");i=e.normalizeModule(r,i);var s=e.require.modules[i];if(s)return s.initialized||(s.initialized=!0,s.exports=s.factory().exports),s.exports;if(!e.require.tlns)return console.log("unable to load "+i);var o=t(i,e.require.tlns);return o.slice(-3)!=".js"&&(o+=".js"),e.require.id=i,e.require.modules[i]={},importScripts(o),e.require(r,i)},e.require.modules={},e.require.tlns={},e.define=function(t,n,r){arguments.length==2?(r=n,typeof t!="string"&&(n=t,t=e.require.id)):arguments.length==1&&(r=t,n=[],t=e.require.id);if(typeof r!="function"){e.require.modules[t]={exports:r,initialized:!0};return}n.length||(n=["require","exports","module"]);var i=function(n){return e.require(t,n)};e.require.modules[t]={exports:{},factory:function(){var e=this,t=r.apply(this,n.map(function(t){switch(t){case"require":return i;case"exports":return e.exports;case"module":return e;default:return i(t)}}));return t&&(e.exports=t),e}}},e.define.amd={},require.tlns={},e.initBaseUrls=function(t){for(var n in t)require.tlns[n]=t[n]},e.initSender=function(){var n=e.require("ace/lib/event_emitter").EventEmitter,r=e.require("ace/lib/oop"),i=function(){};return function(){r.implement(this,n),this.callback=function(e,t){postMessage({type:"call",id:t,data:e})},this.emit=function(e,t){postMessage({type:"event",name:e,data:t})}}.call(i.prototype),new i};var n=e.main=null,r=e.sender=null;e.onmessage=function(t){var i=t.data;if(i.event&&r)r._signal(i.event,i.data);else if(i.command)if(n[i.command])n[i.command].apply(n,i.args);else{if(!e[i.command])throw new Error("Unknown command:"+i.command);e[i.command].apply(e,i.args)}else if(i.init){e.initBaseUrls(i.tlns),require("ace/lib/es5-shim"),r=e.sender=e.initSender();var s=require(i.module)[i.classname];n=e.main=new s(r)}}})(this),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;othis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i=0&&this._ltIndex-1&&!t[u.type].hide&&(u.channel=t[u.type].channel,this._token=u,this._lt.push(u),this._ltIndexCache.push(this._lt.length-this._ltIndex+i),this._lt.length>5&&this._lt.shift(),this._ltIndexCache.length>5&&this._ltIndexCache.shift(),this._ltIndex=this._lt.length),a=t[u.type],a&&(a.hide||a.channel!==undefined&&e!==a.channel)?this.get(e):u.type},LA:function(e){var t=e,n;if(e>0){if(e>5)throw new Error("Too much lookahead.");while(t)n=this.get(),t--;while(tthis._tokenData.length?"UNKNOWN_TOKEN":this._tokenData[e].name},tokenType:function(e){return this._tokenData[e]||-1},unget:function(){if(!this._ltIndexCache.length)throw new Error("Too much lookahead.");this._ltIndex-=this._ltIndexCache.pop(),this._token=this._lt[this._ltIndex-1]}},parserlib.util={StringReader:t,SyntaxError:n,SyntaxUnit:r,EventTarget:e,TokenStreamBase:i}})(),function(){function Combinator(e,t,n){SyntaxUnit.call(this,e,t,n,Parser.COMBINATOR_TYPE),this.type="unknown",/^\s+$/.test(e)?this.type="descendant":e==">"?this.type="child":e=="+"?this.type="adjacent-sibling":e=="~"&&(this.type="sibling")}function MediaFeature(e,t){SyntaxUnit.call(this,"("+e+(t!==null?":"+t:"")+")",e.startLine,e.startCol,Parser.MEDIA_FEATURE_TYPE),this.name=e,this.value=t}function MediaQuery(e,t,n,r,i){SyntaxUnit.call(this,(e?e+" ":"")+(t?t:"")+(t&&n.length>0?" and ":"")+n.join(" and "),r,i,Parser.MEDIA_QUERY_TYPE),this.modifier=e,this.mediaType=t,this.features=n}function Parser(e){EventTarget.call(this),this.options=e||{},this._tokenStream=null}function PropertyName(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.PROPERTY_NAME_TYPE),this.hack=t}function PropertyValue(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.PROPERTY_VALUE_TYPE),this.parts=e}function PropertyValueIterator(e){this._i=0,this._parts=e.parts,this._marks=[],this.value=e}function PropertyValuePart(text,line,col){SyntaxUnit.call(this,text,line,col,Parser.PROPERTY_VALUE_PART_TYPE),this.type="unknown";var temp;if(/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){this.type="dimension",this.value=+RegExp.$1,this.units=RegExp.$2;switch(this.units.toLowerCase()){case"em":case"rem":case"ex":case"px":case"cm":case"mm":case"in":case"pt":case"pc":case"ch":case"vh":case"vw":case"vmax":case"vmin":this.type="length";break;case"deg":case"rad":case"grad":this.type="angle";break;case"ms":case"s":this.type="time";break;case"hz":case"khz":this.type="frequency";break;case"dpi":case"dpcm":this.type="resolution"}}else/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?\d+)$/i.test(text)?(this.type="integer",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)$/i.test(text)?(this.type="number",this.value=+RegExp.$1):/^#([a-f0-9]{3,6})/i.test(text)?(this.type="color",temp=RegExp.$1,temp.length==3?(this.red=parseInt(temp.charAt(0)+temp.charAt(0),16),this.green=parseInt(temp.charAt(1)+temp.charAt(1),16),this.blue=parseInt(temp.charAt(2)+temp.charAt(2),16)):(this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16))):/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3):/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100):/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3,this.alpha=+RegExp.$4):/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100,this.alpha=+RegExp.$4):/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100):/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100,this.alpha=+RegExp.$4):/^url\(["']?([^\)"']+)["']?\)/i.test(text)?(this.type="uri",this.uri=RegExp.$1):/^([^\(]+)\(/i.test(text)?(this.type="function",this.name=RegExp.$1,this.value=text):/^["'][^"']*["']/.test(text)?(this.type="string",this.value=eval(text)):Colors[text.toLowerCase()]?(this.type="color",temp=Colors[text.toLowerCase()].substring(1),this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16)):/^[\,\/]$/.test(text)?(this.type="operator",this.value=text):/^[a-z\-_\u0080-\uFFFF][a-z0-9\-_\u0080-\uFFFF]*$/i.test(text)&&(this.type="identifier",this.value=text)}function Selector(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.SELECTOR_TYPE),this.parts=e,this.specificity=Specificity.calculate(this)}function SelectorPart(e,t,n,r,i){SyntaxUnit.call(this,n,r,i,Parser.SELECTOR_PART_TYPE),this.elementName=e,this.modifiers=t}function SelectorSubPart(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.SELECTOR_SUB_PART_TYPE),this.type=t,this.args=[]}function Specificity(e,t,n,r){this.a=e,this.b=t,this.c=n,this.d=r}function isHexDigit(e){return e!==null&&h.test(e)}function isDigit(e){return e!==null&&/\d/.test(e)}function isWhitespace(e){return e!==null&&/\s/.test(e)}function isNewLine(e){return e!==null&&nl.test(e)}function isNameStart(e){return e!==null&&/[a-z_\u0080-\uFFFF\\]/i.test(e)}function isNameChar(e){return e!==null&&(isNameStart(e)||/[0-9\-\\]/.test(e))}function isIdentStart(e){return e!==null&&(isNameStart(e)||/\-\\/.test(e))}function mix(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function TokenStream(e){TokenStreamBase.call(this,e,Tokens)}function ValidationError(e,t,n){this.col=n,this.line=t,this.message=e}var EventTarget=parserlib.util.EventTarget,TokenStreamBase=parserlib.util.TokenStreamBase,StringReader=parserlib.util.StringReader,SyntaxError=parserlib.util.SyntaxError,SyntaxUnit=parserlib.util.SyntaxUnit,Colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",activeBorder:"Active window border.",activecaption:"Active window caption.",appworkspace:"Background color of multiple document interface.",background:"Desktop background.",buttonface:"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",buttonhighlight:"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",buttonshadow:"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",buttontext:"Text on push buttons.",captiontext:"Text in caption, size box, and scrollbar arrow box.",graytext:"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",greytext:"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",highlight:"Item(s) selected in a control.",highlighttext:"Text of item(s) selected in a control.",inactiveborder:"Inactive window border.",inactivecaption:"Inactive window caption.",inactivecaptiontext:"Color of text in an inactive caption.",infobackground:"Background color for tooltip controls.",infotext:"Text color for tooltip controls.",menu:"Menu background.",menutext:"Text in menus.",scrollbar:"Scroll bar gray area.",threeddarkshadow:"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedface:"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedhighlight:"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedlightshadow:"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",threedshadow:"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",window:"Window background.",windowframe:"Window frame.",windowtext:"Text in windows."};Combinator.prototype=new SyntaxUnit,Combinator.prototype.constructor=Combinator,MediaFeature.prototype=new SyntaxUnit,MediaFeature.prototype.constructor=MediaFeature,MediaQuery.prototype=new SyntaxUnit,MediaQuery.prototype.constructor=MediaQuery,Parser.DEFAULT_TYPE=0,Parser.COMBINATOR_TYPE=1,Parser.MEDIA_FEATURE_TYPE=2,Parser.MEDIA_QUERY_TYPE=3,Parser.PROPERTY_NAME_TYPE=4,Parser.PROPERTY_VALUE_TYPE=5,Parser.PROPERTY_VALUE_PART_TYPE=6,Parser.SELECTOR_TYPE=7,Parser.SELECTOR_PART_TYPE=8,Parser.SELECTOR_SUB_PART_TYPE=9,Parser.prototype=function(){var e=new EventTarget,t,n={constructor:Parser,DEFAULT_TYPE:0,COMBINATOR_TYPE:1,MEDIA_FEATURE_TYPE:2,MEDIA_QUERY_TYPE:3,PROPERTY_NAME_TYPE:4,PROPERTY_VALUE_TYPE:5,PROPERTY_VALUE_PART_TYPE:6,SELECTOR_TYPE:7,SELECTOR_PART_TYPE:8,SELECTOR_SUB_PART_TYPE:9,_stylesheet:function(){var e=this._tokenStream,t=null,n,r,i;this.fire("startstylesheet"),this._charset(),this._skipCruft();while(e.peek()==Tokens.IMPORT_SYM)this._import(),this._skipCruft();while(e.peek()==Tokens.NAMESPACE_SYM)this._namespace(),this._skipCruft();i=e.peek();while(i>Tokens.EOF){try{switch(i){case Tokens.MEDIA_SYM:this._media(),this._skipCruft();break;case Tokens.PAGE_SYM:this._page(),this._skipCruft();break;case Tokens.FONT_FACE_SYM:this._font_face(),this._skipCruft();break;case Tokens.KEYFRAMES_SYM:this._keyframes(),this._skipCruft();break;case Tokens.VIEWPORT_SYM:this._viewport(),this._skipCruft();break;case Tokens.UNKNOWN_SYM:e.get();if(!!this.options.strict)throw new SyntaxError("Unknown @ rule.",e.LT(0).startLine,e.LT(0).startCol);this.fire({type:"error",error:null,message:"Unknown @ rule: "+e.LT(0).value+".",line:e.LT(0).startLine,col:e.LT(0).startCol}),n=0;while(e.advance([Tokens.LBRACE,Tokens.RBRACE])==Tokens.LBRACE)n++;while(n)e.advance([Tokens.RBRACE]),n--;break;case Tokens.S:this._readWhitespace();break;default:if(!this._ruleset())switch(i){case Tokens.CHARSET_SYM:throw r=e.LT(1),this._charset(!1),new SyntaxError("@charset not allowed here.",r.startLine,r.startCol);case Tokens.IMPORT_SYM:throw r=e.LT(1),this._import(!1),new SyntaxError("@import not allowed here.",r.startLine,r.startCol);case Tokens.NAMESPACE_SYM:throw r=e.LT(1),this._namespace(!1),new SyntaxError("@namespace not allowed here.",r.startLine,r.startCol);default:e.get(),this._unexpectedToken(e.token())}}}catch(s){if(!(s instanceof SyntaxError&&!this.options.strict))throw s;this.fire({type:"error",error:s,message:s.message,line:s.line,col:s.col})}i=e.peek()}i!=Tokens.EOF&&this._unexpectedToken(e.token()),this.fire("endstylesheet")},_charset:function(e){var t=this._tokenStream,n,r,i,s;t.match(Tokens.CHARSET_SYM)&&(i=t.token().startLine,s=t.token().startCol,this._readWhitespace(),t.mustMatch(Tokens.STRING),r=t.token(),n=r.value,this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),e!==!1&&this.fire({type:"charset",charset:n,line:i,col:s}))},_import:function(e){var t=this._tokenStream,n,r,i,s=[];t.mustMatch(Tokens.IMPORT_SYM),i=t.token(),this._readWhitespace(),t.mustMatch([Tokens.STRING,Tokens.URI]),r=t.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/,"$1"),this._readWhitespace(),s=this._media_query_list(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"import",uri:r,media:s,line:i.startLine,col:i.startCol})},_namespace:function(e){var t=this._tokenStream,n,r,i,s;t.mustMatch(Tokens.NAMESPACE_SYM),n=t.token().startLine,r=t.token().startCol,this._readWhitespace(),t.match(Tokens.IDENT)&&(i=t.token().value,this._readWhitespace()),t.mustMatch([Tokens.STRING,Tokens.URI]),s=t.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"namespace",prefix:i,uri:s,line:n,col:r})},_media:function(){var e=this._tokenStream,t,n,r;e.mustMatch(Tokens.MEDIA_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),r=this._media_query_list(),e.mustMatch(Tokens.LBRACE),this._readWhitespace(),this.fire({type:"startmedia",media:r,line:t,col:n});for(;;)if(e.peek()==Tokens.PAGE_SYM)this._page();else if(e.peek()==Tokens.FONT_FACE_SYM)this._font_face();else if(e.peek()==Tokens.VIEWPORT_SYM)this._viewport();else if(!this._ruleset())break;e.mustMatch(Tokens.RBRACE),this._readWhitespace(),this.fire({type:"endmedia",media:r,line:t,col:n})},_media_query_list:function(){var e=this._tokenStream,t=[];this._readWhitespace(),(e.peek()==Tokens.IDENT||e.peek()==Tokens.LPAREN)&&t.push(this._media_query());while(e.match(Tokens.COMMA))this._readWhitespace(),t.push(this._media_query());return t},_media_query:function(){var e=this._tokenStream,t=null,n=null,r=null,i=[];e.match(Tokens.IDENT)&&(n=e.token().value.toLowerCase(),n!="only"&&n!="not"?(e.unget(),n=null):r=e.token()),this._readWhitespace(),e.peek()==Tokens.IDENT?(t=this._media_type(),r===null&&(r=e.token())):e.peek()==Tokens.LPAREN&&(r===null&&(r=e.LT(1)),i.push(this._media_expression()));if(t===null&&i.length===0)return null;this._readWhitespace();while(e.match(Tokens.IDENT))e.token().value.toLowerCase()!="and"&&this._unexpectedToken(e.token()),this._readWhitespace(),i.push(this._media_expression());return new MediaQuery(n,t,i,r.startLine,r.startCol)},_media_type:function(){return this._media_feature()},_media_expression:function(){var e=this._tokenStream,t=null,n,r=null;return e.mustMatch(Tokens.LPAREN),t=this._media_feature(),this._readWhitespace(),e.match(Tokens.COLON)&&(this._readWhitespace(),n=e.LT(1),r=this._expression()),e.mustMatch(Tokens.RPAREN),this._readWhitespace(),new MediaFeature(t,r?new SyntaxUnit(r,n.startLine,n.startCol):null)},_media_feature:function(){var e=this._tokenStream;return e.mustMatch(Tokens.IDENT),SyntaxUnit.fromToken(e.token())},_page:function(){var e=this._tokenStream,t,n,r=null,i=null;e.mustMatch(Tokens.PAGE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),e.match(Tokens.IDENT)&&(r=e.token().value,r.toLowerCase()==="auto"&&this._unexpectedToken(e.token())),e.peek()==Tokens.COLON&&(i=this._pseudo_page()),this._readWhitespace(),this.fire({type:"startpage",id:r,pseudo:i,line:t,col:n}),this._readDeclarations(!0,!0),this.fire({type:"endpage",id:r,pseudo:i,line:t,col:n})},_margin:function(){var e=this._tokenStream,t,n,r=this._margin_sym();return r?(t=e.token().startLine,n=e.token().startCol,this.fire({type:"startpagemargin",margin:r,line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endpagemargin",margin:r,line:t,col:n}),!0):!1},_margin_sym:function(){var e=this._tokenStream;return e.match([Tokens.TOPLEFTCORNER_SYM,Tokens.TOPLEFT_SYM,Tokens.TOPCENTER_SYM,Tokens.TOPRIGHT_SYM,Tokens.TOPRIGHTCORNER_SYM,Tokens.BOTTOMLEFTCORNER_SYM,Tokens.BOTTOMLEFT_SYM,Tokens.BOTTOMCENTER_SYM,Tokens.BOTTOMRIGHT_SYM,Tokens.BOTTOMRIGHTCORNER_SYM,Tokens.LEFTTOP_SYM,Tokens.LEFTMIDDLE_SYM,Tokens.LEFTBOTTOM_SYM,Tokens.RIGHTTOP_SYM,Tokens.RIGHTMIDDLE_SYM,Tokens.RIGHTBOTTOM_SYM])?SyntaxUnit.fromToken(e.token()):null},_pseudo_page:function(){var e=this._tokenStream;return e.mustMatch(Tokens.COLON),e.mustMatch(Tokens.IDENT),e.token().value},_font_face:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.FONT_FACE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startfontface",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endfontface",line:t,col:n})},_viewport:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.VIEWPORT_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startviewport",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endviewport",line:t,col:n})},_operator:function(e){var t=this._tokenStream,n=null;if(t.match([Tokens.SLASH,Tokens.COMMA])||e&&t.match([Tokens.PLUS,Tokens.STAR,Tokens.MINUS]))n=t.token(),this._readWhitespace();return n?PropertyValuePart.fromToken(n):null},_combinator:function(){var e=this._tokenStream,t=null,n;return e.match([Tokens.PLUS,Tokens.GREATER,Tokens.TILDE])&&(n=e.token(),t=new Combinator(n.value,n.startLine,n.startCol),this._readWhitespace()),t},_unary_operator:function(){var e=this._tokenStream;return e.match([Tokens.MINUS,Tokens.PLUS])?e.token().value:null},_property:function(){var e=this._tokenStream,t=null,n=null,r,i,s,o;return e.peek()==Tokens.STAR&&this.options.starHack&&(e.get(),i=e.token(),n=i.value,s=i.startLine,o=i.startCol),e.match(Tokens.IDENT)&&(i=e.token(),r=i.value,r.charAt(0)=="_"&&this.options.underscoreHack&&(n="_",r=r.substring(1)),t=new PropertyName(r,n,s||i.startLine,o||i.startCol),this._readWhitespace()),t},_ruleset:function(){var e=this._tokenStream,t,n;try{n=this._selectors_group()}catch(r){if(r instanceof SyntaxError&&!this.options.strict){this.fire({type:"error",error:r,message:r.message,line:r.line,col:r.col}),t=e.advance([Tokens.RBRACE]);if(t!=Tokens.RBRACE)throw r;return!0}throw r}return n&&(this.fire({type:"startrule",selectors:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endrule",selectors:n,line:n[0].line,col:n[0].col})),n},_selectors_group:function(){var e=this._tokenStream,t=[],n;n=this._selector();if(n!==null){t.push(n);while(e.match(Tokens.COMMA))this._readWhitespace(),n=this._selector(),n!==null?t.push(n):this._unexpectedToken(e.LT(1))}return t.length?t:null},_selector:function(){var e=this._tokenStream,t=[],n=null,r=null,i=null;n=this._simple_selector_sequence();if(n===null)return null;t.push(n);do{r=this._combinator();if(r!==null)t.push(r),n=this._simple_selector_sequence(),n===null?this._unexpectedToken(e.LT(1)):t.push(n);else{if(!this._readWhitespace())break;i=new Combinator(e.token().value,e.token().startLine,e.token().startCol),r=this._combinator(),n=this._simple_selector_sequence(),n===null?r!==null&&this._unexpectedToken(e.LT(1)):(r!==null?t.push(r):t.push(i),t.push(n))}}while(!0);return new Selector(t,t[0].line,t[0].col)},_simple_selector_sequence:function(){var e=this._tokenStream,t=null,n=[],r="",i=[function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo,this._negation],s=0,o=i.length,u=null,a=!1,f,l;f=e.LT(1).startLine,l=e.LT(1).startCol,t=this._type_selector(),t||(t=this._universal()),t!==null&&(r+=t);for(;;){if(e.peek()===Tokens.S)break;while(s1&&e.unget()),null)},_class:function(){var e=this._tokenStream,t;return e.match(Tokens.DOT)?(e.mustMatch(Tokens.IDENT),t=e.token(),new SelectorSubPart("."+t.value,"class",t.startLine,t.startCol-1)):null},_element_name:function(){var e=this._tokenStream,t;return e.match(Tokens.IDENT)?(t=e.token(),new SelectorSubPart(t.value,"elementName",t.startLine,t.startCol)):null},_namespace_prefix:function(){var e=this._tokenStream,t="";if(e.LA(1)===Tokens.PIPE||e.LA(2)===Tokens.PIPE)e.match([Tokens.IDENT,Tokens.STAR])&&(t+=e.token().value),e.mustMatch(Tokens.PIPE),t+="|";return t.length?t:null},_universal:function(){var e=this._tokenStream,t="",n;return n=this._namespace_prefix(),n&&(t+=n),e.match(Tokens.STAR)&&(t+="*"),t.length?t:null},_attrib:function(){var e=this._tokenStream,t=null,n,r;return e.match(Tokens.LBRACKET)?(r=e.token(),t=r.value,t+=this._readWhitespace(),n=this._namespace_prefix(),n&&(t+=n),e.mustMatch(Tokens.IDENT),t+=e.token().value,t+=this._readWhitespace(),e.match([Tokens.PREFIXMATCH,Tokens.SUFFIXMATCH,Tokens.SUBSTRINGMATCH,Tokens.EQUALS,Tokens.INCLUDES,Tokens.DASHMATCH])&&(t+=e.token().value,t+=this._readWhitespace(),e.mustMatch([Tokens.IDENT,Tokens.STRING]),t+=e.token().value,t+=this._readWhitespace()),e.mustMatch(Tokens.RBRACKET),new SelectorSubPart(t+"]","attribute",r.startLine,r.startCol)):null},_pseudo:function(){var e=this._tokenStream,t=null,n=":",r,i;return e.match(Tokens.COLON)&&(e.match(Tokens.COLON)&&(n+=":"),e.match(Tokens.IDENT)?(t=e.token().value,r=e.token().startLine,i=e.token().startCol-n.length):e.peek()==Tokens.FUNCTION&&(r=e.LT(1).startLine,i=e.LT(1).startCol-n.length,t=this._functional_pseudo()),t&&(t=new SelectorSubPart(n+t,"pseudo",r,i))),t},_functional_pseudo:function(){var e=this._tokenStream,t=null;return e.match(Tokens.FUNCTION)&&(t=e.token().value,t+=this._readWhitespace(),t+=this._expression(),e.mustMatch(Tokens.RPAREN),t+=")"),t},_expression:function(){var e=this._tokenStream,t="";while(e.match([Tokens.PLUS,Tokens.MINUS,Tokens.DIMENSION,Tokens.NUMBER,Tokens.STRING,Tokens.IDENT,Tokens.LENGTH,Tokens.FREQ,Tokens.ANGLE,Tokens.TIME,Tokens.RESOLUTION,Tokens.SLASH]))t+=e.token().value,t+=this._readWhitespace();return t.length?t:null},_negation:function(){var e=this._tokenStream,t,n,r="",i,s=null;return e.match(Tokens.NOT)&&(r=e.token().value,t=e.token().startLine,n=e.token().startCol,r+=this._readWhitespace(),i=this._negation_arg(),r+=i,r+=this._readWhitespace(),e.match(Tokens.RPAREN),r+=e.token().value,s=new SelectorSubPart(r,"not",t,n),s.args.push(i)),s},_negation_arg:function(){var e=this._tokenStream,t=[this._type_selector,this._universal,function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo],n=null,r=0,i=t.length,s,o,u,a;o=e.LT(1).startLine,u=e.LT(1).startCol;while(r0?new PropertyValue(n,n[0].line,n[0].col):null},_term:function(e){var t=this._tokenStream,n=null,r=null,i=null,s,o,u;return n=this._unary_operator(),n!==null&&(o=t.token().startLine,u=t.token().startCol),t.peek()==Tokens.IE_FUNCTION&&this.options.ieFilters?(r=this._ie_function(),n===null&&(o=t.token().startLine,u=t.token().startCol)):e&&t.match([Tokens.LPAREN,Tokens.LBRACE,Tokens.LBRACKET])?(s=t.token(),i=s.endChar,r=s.value+this._expr(e).text,n===null&&(o=t.token().startLine,u=t.token().startCol),t.mustMatch(Tokens.type(i)),r+=i,this._readWhitespace()):t.match([Tokens.NUMBER,Tokens.PERCENTAGE,Tokens.LENGTH,Tokens.ANGLE,Tokens.TIME,Tokens.FREQ,Tokens.STRING,Tokens.IDENT,Tokens.URI,Tokens.UNICODE_RANGE])?(r=t.token().value,n===null&&(o=t.token().startLine,u=t.token().startCol),this._readWhitespace()):(s=this._hexcolor(),s===null?(n===null&&(o=t.LT(1).startLine,u=t.LT(1).startCol),r===null&&(t.LA(3)==Tokens.EQUALS&&this.options.ieFilters?r=this._ie_function():r=this._function())):(r=s.value,n===null&&(o=s.startLine,u=s.startCol))),r!==null?new PropertyValuePart(n!==null?n+r:r,o,u):null},_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match(Tokens.FUNCTION)){t=e.token().value,this._readWhitespace(),n=this._expr(!0),t+=n;if(this.options.ieFilters&&e.peek()==Tokens.EQUALS)do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_ie_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match([Tokens.IE_FUNCTION,Tokens.FUNCTION])){t=e.token().value;do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_hexcolor:function(){var e=this._tokenStream,t=null,n;if(e.match(Tokens.HASH)){t=e.token(),n=t.value;if(!/#[a-f0-9]{3,6}/i.test(n))throw new SyntaxError("Expected a hex color but found '"+n+"' at line "+t.startLine+", col "+t.startCol+".",t.startLine,t.startCol);this._readWhitespace()}return t},_keyframes:function(){var e=this._tokenStream,t,n,r,i="";e.mustMatch(Tokens.KEYFRAMES_SYM),t=e.token(),/^@\-([^\-]+)\-/.test(t.value)&&(i=RegExp.$1),this._readWhitespace(),r=this._keyframe_name(),this._readWhitespace(),e.mustMatch(Tokens.LBRACE),this.fire({type:"startkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),n=e.peek();while(n==Tokens.IDENT||n==Tokens.PERCENTAGE)this._keyframe_rule(),this._readWhitespace(),n=e.peek();this.fire({type:"endkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),e.mustMatch(Tokens.RBRACE)},_keyframe_name:function(){var e=this._tokenStream,t;return e.mustMatch([Tokens.IDENT,Tokens.STRING]),SyntaxUnit.fromToken(e.token())},_keyframe_rule:function(){var e=this._tokenStream,t,n=this._key_list();this.fire({type:"startkeyframerule",keys:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endkeyframerule",keys:n,line:n[0].line,col:n[0].col})},_key_list:function(){var e=this._tokenStream,t,n,r=[];r.push(this._key()),this._readWhitespace();while(e.match(Tokens.COMMA))this._readWhitespace(),r.push(this._key()),this._readWhitespace();return r},_key:function(){var e=this._tokenStream,t;if(e.match(Tokens.PERCENTAGE))return SyntaxUnit.fromToken(e.token());if(e.match(Tokens.IDENT)){t=e.token();if(/from|to/i.test(t.value))return SyntaxUnit.fromToken(t);e.unget()}this._unexpectedToken(e.LT(1))},_skipCruft:function(){while(this._tokenStream.match([Tokens.S,Tokens.CDO,Tokens.CDC]));},_readDeclarations:function(e,t){var n=this._tokenStream,r;this._readWhitespace(),e&&n.mustMatch(Tokens.LBRACE),this._readWhitespace();try{for(;;){if(!(n.match(Tokens.SEMICOLON)||t&&this._margin())){if(!this._declaration())break;if(!n.match(Tokens.SEMICOLON))break}this._readWhitespace()}n.mustMatch(Tokens.RBRACE),this._readWhitespace()}catch(i){if(!(i instanceof SyntaxError&&!this.options.strict))throw i;this.fire({type:"error",error:i,message:i.message,line:i.line,col:i.col}),r=n.advance([Tokens.SEMICOLON,Tokens.RBRACE]);if(r==Tokens.SEMICOLON)this._readDeclarations(!1,t);else if(r!=Tokens.RBRACE)throw i}},_readWhitespace:function(){var e=this._tokenStream,t="";while(e.match(Tokens.S))t+=e.token().value;return t},_unexpectedToken:function(e){throw new SyntaxError("Unexpected token '"+e.value+"' at line "+e.startLine+", col "+e.startCol+".",e.startLine,e.startCol)},_verifyEnd:function(){this._tokenStream.LA(1)!=Tokens.EOF&&this._unexpectedToken(this._tokenStream.LT(1))},_validateProperty:function(e,t){Validation.validate(e,t)},parse:function(e){this._tokenStream=new TokenStream(e,Tokens),this._stylesheet()},parseStyleSheet:function(e){return this.parse(e)},parseMediaQuery:function(e){this._tokenStream=new TokenStream(e,Tokens);var t=this._media_query();return this._verifyEnd(),t},parsePropertyValue:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._expr();return this._readWhitespace(),this._verifyEnd(),t},parseRule:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._ruleset();return this._readWhitespace(),this._verifyEnd(),t},parseSelector:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._selector();return this._readWhitespace(),this._verifyEnd(),t},parseStyleAttribute:function(e){e+="}",this._tokenStream=new TokenStream(e,Tokens),this._readDeclarations()}};for(t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e}();var Properties={"align-items":"flex-start | flex-end | center | baseline | stretch","align-content":"flex-start | flex-end | center | space-between | space-around | stretch","align-self":"auto | flex-start | flex-end | center | baseline | stretch","-webkit-align-items":"flex-start | flex-end | center | baseline | stretch","-webkit-align-content":"flex-start | flex-end | center | space-between | space-around | stretch","-webkit-align-self":"auto | flex-start | flex-end | center | baseline | stretch","alignment-adjust":"auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ","alignment-baseline":"baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",animation:1,"animation-delay":{multi:"