diff --git a/app.js b/app.js index c6e0647..137f16a 100755 --- a/app.js +++ b/app.js @@ -5,18 +5,28 @@ const cors = require('cors'); const routes = require('./routes'); const hbs = require('express-handlebars'); const bodyParser = require('body-parser'); +// const i18n = require('i18n'); +const { I18n } = require('i18n'); const app = express(); - // Configuration // - //app.use(cors()); //app.use(bodyParser.json()); //app.use(session({ secret: 'slartibartfast', cookie: { maxAge: 60000 }, resave: false, saveUninitialized: false })); +app.use(session({ + secret: 'Py0Bf3aWZC8kYkYTpRztmYMyS22pFFGi' +})); +// Internationalization // +const i18n = new I18n({ + locales:['en-US'], //include langs + directory: path.join(__dirname, 'locales'), + defaultLocale: 'en-US' +}); +app.use(i18n.init); // View engine // -hbsInstance = hbs.create({ +const hbsInstance = hbs.create({ defaultLayout: 'main', partialsDir: ['views/partials/'], layoutsDir: 'views/layouts/', @@ -51,4 +61,34 @@ app.use(bodyParser.json({ type: "application/activity+json" })); // support json app.use(bodyParser.urlencoded({ extended: true })); app.use('/', routes); +app.use(setLocale); module.exports = app; + +// def setLocale +function setLocale(req, res, next){ + var locale; + // Get the locale data in the user data + if(req.user){ + locale = req.user.locale; + } + // Get the locale data in the cookie + else if(req.signedCookies['locale']){ + locale = req.signedCookies['locale']; + } + // Get the first preferred language of the browser, this function is provided by express + // User-selected languages will be added later + else if(req.acceptsLanguages()){ + locale = req.acceptsLanguages(); + } + // When there is no language preference, the language used on the website is English + else { + locale = 'en-US'; + } + // If the language preference saved in the cookie is different from the language preference used here, update the language preference setting in the cookie + if(req.signedCookies['locale'] !== locale){ + res.cookie('locale', locale, { signed: true, httpOnly: true }); + } + // Set the language that i18n will use for this request + req.setLocale(locale); + next(); +}; \ No newline at end of file diff --git a/config/api-example.js b/config/api-example.js index 9202f0a..4f66ff8 100644 --- a/config/api-example.js +++ b/config/api-example.js @@ -4,5 +4,6 @@ module.exports = { 'smtpServer': '', // If using Nodemailer, your SMTP server hostname goes here 'smtpPort': '', // If using Nodemailer, your SMTP server port goes here 'smtpUsername': '', // If using Nodemailer, your SMTP server username goes here - 'smtpPassword': '' // If using Nodemailer, your SMTP password goes here + 'smtpPassword': '', // If using Nodemailer, your SMTP password goes here + 'smtpSecure': true // true for 465, false for other ports }; diff --git a/helpers.js b/helpers.js index bf95e27..7667b9f 100644 --- a/helpers.js +++ b/helpers.js @@ -5,7 +5,7 @@ const mongoose = require('mongoose'); const Log = mongoose.model('Log'); var moment = require('moment-timezone'); const icalGenerator = require('ical-generator'); - +var i18n = require('i18n'); // LOGGING function addToLog(process, status, message) { @@ -51,7 +51,23 @@ function exportIcal(events, calendarName) { return string; } +function getI18nHelpers() { + var _helpers = {}; + // Declare the i18n helper function in the handlebar + // __ Not considering singular and plural + _helpers.__ = function () { + return i18n.__.apply(this, arguments); + }; + // __n Considering singular and plural + _helpers.__n = function () { + return i18n.__n.apply(this, arguments); + }; + + return _helpers; +} + module.exports = { addToLog, exportIcal, -} + getI18nHelpers, +} \ No newline at end of file diff --git a/locales/en-US.json b/locales/en-US.json new file mode 100644 index 0000000..d7b90f8 --- /dev/null +++ b/locales/en-US.json @@ -0,0 +1,131 @@ +{ + "notFound": "Event not found!", + "404Desc": "It may have never existed, or if it finished more than a week ago, it's been removed from the server. Don't despair - why not create a new one? I for one would love to come to your ocarina recital.", + "editEvent": "Edit event", + "started": "Started", + "ended": "Ended", + "hostedBy": "Hosted by {{eventData.hostName}}", + "partOf": "Part of {{eventData.eventGroup.name}}", + "copy": "Copy", + "addToGC": "Add to Google Calendar", + "exportAsICS": "Export as ICS", + "showOnGM": "Show on Google Maps", + "showOnOSM": "Show on OpenStreetMap", + "concludedDel": "This event has concluded. It can no longer be edited, and will be automatically deleted .", + "eventWelcome": "Welcome to your event!", + "eventPasswordAlert": "Your secret editing password for this event is: {{eventData.editToken}}. It's been saved in your browser storage, and if you supplied your email, it's also been sent to you. If you didn't supply your email, you must save it somewhere safe, because it won't be shown again!", + "eventShare": "To share your event, use the link you can see just above this message - that way your attendees won't be able to edit or delete your event!", + "about": "About", + "attendees": "Attendees", + "addMe": "Add me", + "removeMe": "Remove me", + "eventAtCapacity": "This event is at capacity.", + "remaining": "remaining - add yourself now!", + "removeUser": "Remove user from event", + "noAttendees": "No attendees yet!", + "addSelfTitle": "Add yourself to '{{eventData.name}}'", + "yourName": "Your name", + "peopleNum": "How many people in your party?", + "yourEmail": "Your email", + "joinEmailDesc": "If you provide your email, you will receive updates to the event.", + "optional": "Optional.", + "delPassword": "Deletion password", + "delPasswordDesc": "You will need this password if you want to remove yourself from the list of event attendees. If you provided your email, you'll receive it by email. Otherwise, write it down now because it will not be shown again.", + "close": "Close", + "addSelf": "Add myself", + "removeSelfTitle": "Remove yourself from '{{eventData.name}}'", + "lostPassword": "Lost your password? Get in touch with the event organiser.", + "removeSelf": "Remove myself", + "removeAttendeeTitle": "Remove attendee from '{{eventData.name}}'", + "confirmRemoveAttendee": "Are you sure you want to remove this attendee from the event? This action cannot be undone.", + "removeAttendee": "Remove attendee", + "discussion": "Discussion", + "send": "Send", + "reply": "Reply", + "del": "Delete", + "editPassword": "Enter editing password", + "editPasswordDesc": "Enter the editing password you received by email or were shown when the event was created.", + "confirmDelEvent": "Are you sure you want to delete this event? This action cannot be undone.", + "delEvent": "Delete event", + "copied": "Copied!", + "incorrectPassword": "That editing password is incorrect. Try again.", + "numValidation": "Please enter a number between 1 and ${response.data.freeSpots}", + "editGroup": "Edit group", + "groupWelcome": "Welcome to your event group! We've just sent you an email with your secret editing link, which you can also see in the address bar above. Haven't got the email? Check your spam or junk folder. To share your event group, use the link you can see just below this message - that way your attendees won't be able to edit or delete your event group!", + "urlPaste": "Paste this URL into your calendar app to subscribe to a live feed of events from this group.", + "subscribe": "Subscribe to updates", + "groupAddEvent": "To add an event to this group, copy and paste the two codes below into the \"Event Group\" box when creating a new event or editing an existing event.", + "eventGroupId": "Event group ID", + "eventGroupEditPassword": "Event group editing password", + "upcomingEvents": "Upcoming events", + "noEvents": "No events!", + "groupDelConfirm": "Are you sure you want to delete this event group? This action cannot be undone.", + "groupDelDesc": "This will not delete the individual events contained in this group. They can be linked to another group later.", + "groupDel": "Delete event group", + "groupSub": "Subscribe to '{{eventGroupData.name}}'", + "receiveGroupUpdates": "Enter your email address to receive updates whenever a new event is created in this group.", + "sub": "Subscribe", + "homeIntro": "A quick and easy way to make and share events which respects your privacy.", + "homeDescription": "You don't need to sign up for an account. When you create an event, we generate a password which allows you to edit the event. Send all your guests the public link, and all your co-hosts the secret editing link containing the password. A week after the event finishes, it's deleted from our servers for ever, and your email goes with it.", + "homePrivacy": "Also, we don't show you ads, don't sell your data, and never send you unnecessary emails.", + "homeVisibility": "But remember: all our events are visible to anyone who knows the link, so probably don't use gathio to plot your surprise birthday party or revolution. Or whatever, you do you.", + "homeSupport": "If you find yourself using and enjoying gathio, consider buying me a coffee. It'll help keep the site running!", + "homeKofi": "Support Me on Ko-fi", + "newEvent": "New event", + "newEventDesc": "Events are visible to anyone who knows the link.", + "createEvent": "Create a new event", + "importEvent": "Import an existing event", + "createGroup": "Create a new event group", + "options": "Options", + "usersCanAttend": "Users can mark themselves as attending this event", + "usersCanComment": "Users can post comments on this event", + "listdisplay": "Display the list of attendees", + "edit": "Edit", + "groupName": "Group name", + "description": "Description", + "link": "Link", + "host": "Host or organisation name", + "coverImage": "Cover image", + "recommendedDimensions": "Recommended dimensions (w x h): 920px by 300px.", + "delGroup": "Delete event group", + "save": "Save changes", + "eventName": "Event name", + "location": "Location", + "starts": "Starts", + "ends": "Ends", + "timezone": "Timezone", + "markdownSupported": "Markdown formatting supported.", + "hostName": "Host name", + "chooseFile": "Choose file", + "delImage": "Delete image", + "partOfEventGroup": "This event is part of an event group", + "linkToEventGroup": "Link this event to an event group", + "shortStringDesc": "You can find this short string of characters in the event group's link, in your confirmation email, or on the event group's page.", + "groupSecretCode": "Event group secret editing code", + "longStringDesc": "You can find this long string of characters in the confirmation email you received when you created the event group.", + "setAttendeeLimit": "Set a limit on the maximum number of attendees", + "attendeeLimit": "Attendee limit", + "importDesc": "Upload an .ics file here to instantly create an event. You can save a Facebook event as an .ics file by clicking on the context menu next to the \"Import\" and \"Edit\" buttons on the event page and choosing the \"Export Event\" option. Then select the \"Save to calendar\" option and save the file on your computer.", + "selectFile": "Select file", + "import": "Import", + "emailDesc": "If you provide your email, we will send your secret editing password here, and use it to notify you of updates to the event.", + "emailDescGroup": "If you provide your email, we will send your secret editing password here.", + "changeFile": "Change file", + "groupDesc": "An event group is a holding area for a set of linked events, like a series of film nights, a festival, or a band tour. You can share a public link to your event group just like an individual event link, and people who know the secret editing code (sent in an email when you create the event group) will be able to add future events to the group.", + "groupRemovalNotice": "Event groups do not get automatically removed like events do, but events which have been removed from {{siteName}} will of course not show up in an event group.", + "nicerEvents": "Nicer events", + "create": "Create", + "orAlias": "Or an alias, perhaps...", + "commentPlaceholder": "What would you like to ask?", + "replyPlaceholder": "What would you like to reply?", + "right": "Get it right!", + "snappy": "Make it snappy.", + "linkDescGroup": "For tickets or a page with more information (optional).", + "willBeShownGroup": "Will be shown on the event group page (optional).", + "specific": "Be specific.", + "linkDesc": "For tickets or another event page (optional).", + "willBeShownEvent": "Will be shown on the event page (optional).", + "enterNumber": "Enter a number.", + "click": "Click me!", + "editLater": "You can always edit it later." +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b19767b..7145052 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "generate-rsa-keypair": "^0.2.1", "greenlock": "^2.8.8", "greenlock-express": "^2.7.18", + "i18n": "^0.15.1", "ical": "^0.6.0", "ical-generator": "^1.11.0", "jimp": "^0.16.1", @@ -580,6 +581,45 @@ "regenerator-runtime": "^0.13.3" } }, + "node_modules/@messageformat/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.1.0.tgz", + "integrity": "sha512-UxAnjecnRG4u2iaggwIyylYPHmk5BTErJcKmWyAKTXqYgSW1bFLp4D7fIzuh6bk17Qfcmf3qtufdrstCB23nBA==", + "dependencies": { + "@messageformat/date-skeleton": "^1.0.0", + "@messageformat/number-skeleton": "^1.0.0", + "@messageformat/parser": "^5.0.0", + "@messageformat/runtime": "^3.0.1", + "make-plural": "^7.0.0", + "safe-identifier": "^0.4.1" + } + }, + "node_modules/@messageformat/date-skeleton": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz", + "integrity": "sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==" + }, + "node_modules/@messageformat/number-skeleton": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.1.0.tgz", + "integrity": "sha512-F0Io+GOSvFFxvp9Ze3L5kAoZ2NnOAT0Mr/jpGNd3fqo8A0t4NxNIAcCdggtl2B/gN2ErkIKSBVPrF7xcW1IGvA==" + }, + "node_modules/@messageformat/parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.0.0.tgz", + "integrity": "sha512-WiDKhi8F0zQaFU8cXgqq69eYFarCnTVxKcvhAONufKf0oUxbqLMW6JX6rV4Hqh+BEQWGyKKKHY4g1XA6bCLylA==", + "dependencies": { + "moo": "^0.5.1" + } + }, + "node_modules/@messageformat/runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz", + "integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==", + "dependencies": { + "make-plural": "^7.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -974,6 +1014,11 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1920,6 +1965,17 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "dependencies": { + "boolean": "^3.1.4" + }, + "engines": { + "node": ">=10.0" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -2364,6 +2420,46 @@ "npm": ">=1.3.7" } }, + "node_modules/i18n": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz", + "integrity": "sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA==", + "dependencies": { + "@messageformat/core": "^3.0.0", + "debug": "^4.3.3", + "fast-printf": "^1.6.9", + "make-plural": "^7.0.0", + "math-interval-parser": "^2.0.1", + "mustache": "^4.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/mashpie" + } + }, + "node_modules/i18n/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/i18n/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ical": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/ical/-/ical-0.6.0.tgz", @@ -2843,6 +2939,11 @@ "node": "*" } }, + "node_modules/make-plural": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.2.0.tgz", + "integrity": "sha512-WkdI+iaWaBCFM2wUXwos8Z7spg5Dt64Xe/VI6NpRaly21cDtD76N6S97K//UtzV0dHOiXX+E90TnszdXHG0aMg==" + }, "node_modules/marked": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", @@ -2854,6 +2955,14 @@ "node": ">= 12" } }, + "node_modules/math-interval-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", + "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3059,6 +3168,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "node_modules/mpath": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", @@ -3117,6 +3231,14 @@ "node": ">= 6.0.0" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -3915,6 +4037,11 @@ } ] }, + "node_modules/safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==" + }, "node_modules/safe-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz", @@ -4931,6 +5058,45 @@ "regenerator-runtime": "^0.13.3" } }, + "@messageformat/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.1.0.tgz", + "integrity": "sha512-UxAnjecnRG4u2iaggwIyylYPHmk5BTErJcKmWyAKTXqYgSW1bFLp4D7fIzuh6bk17Qfcmf3qtufdrstCB23nBA==", + "requires": { + "@messageformat/date-skeleton": "^1.0.0", + "@messageformat/number-skeleton": "^1.0.0", + "@messageformat/parser": "^5.0.0", + "@messageformat/runtime": "^3.0.1", + "make-plural": "^7.0.0", + "safe-identifier": "^0.4.1" + } + }, + "@messageformat/date-skeleton": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz", + "integrity": "sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==" + }, + "@messageformat/number-skeleton": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.1.0.tgz", + "integrity": "sha512-F0Io+GOSvFFxvp9Ze3L5kAoZ2NnOAT0Mr/jpGNd3fqo8A0t4NxNIAcCdggtl2B/gN2ErkIKSBVPrF7xcW1IGvA==" + }, + "@messageformat/parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.0.0.tgz", + "integrity": "sha512-WiDKhi8F0zQaFU8cXgqq69eYFarCnTVxKcvhAONufKf0oUxbqLMW6JX6rV4Hqh+BEQWGyKKKHY4g1XA6bCLylA==", + "requires": { + "moo": "^0.5.1" + } + }, + "@messageformat/runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz", + "integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==", + "requires": { + "make-plural": "^7.0.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5252,6 +5418,11 @@ "unpipe": "1.0.0" } }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5956,6 +6127,14 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "requires": { + "boolean": "^3.1.4" + } + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -6290,6 +6469,34 @@ "sshpk": "^1.7.0" } }, + "i18n": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz", + "integrity": "sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA==", + "requires": { + "@messageformat/core": "^3.0.0", + "debug": "^4.3.3", + "fast-printf": "^1.6.9", + "make-plural": "^7.0.0", + "math-interval-parser": "^2.0.1", + "mustache": "^4.2.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ical": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/ical/-/ical-0.6.0.tgz", @@ -6673,11 +6880,21 @@ "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", "optional": true }, + "make-plural": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.2.0.tgz", + "integrity": "sha512-WkdI+iaWaBCFM2wUXwos8Z7spg5Dt64Xe/VI6NpRaly21cDtD76N6S97K//UtzV0dHOiXX+E90TnszdXHG0aMg==" + }, "marked": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==" }, + "math-interval-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", + "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6817,6 +7034,11 @@ "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", "requires": {} }, + "moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "mpath": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", @@ -6868,6 +7090,11 @@ "xtend": "^4.0.0" } }, + "mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" + }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -7444,6 +7671,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==" + }, "safe-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz", diff --git a/package.json b/package.json index 8963fd2..127195a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "generate-rsa-keypair": "^0.2.1", "greenlock": "^2.8.8", "greenlock-express": "^2.7.18", + "i18n": "^0.15.1", "ical": "^0.6.0", "ical-generator": "^1.11.0", "jimp": "^0.16.1", diff --git a/routes.js b/routes.js index c9867e3..05b2919 100755 --- a/routes.js +++ b/routes.js @@ -103,7 +103,7 @@ if (mailService) { nodemailerTransporter = nodemailer.createTransport({ host: apiCredentials.smtpServer, port: apiCredentials.smtpPort, - secure: false, // true for 465, false for other ports + secure: apiCredentials.smtpSecure, // true for 465, false for other ports auth: { user: apiCredentials.smtpUsername, // generated ethereal user pass: apiCredentials.smtpPassword, // generated ethereal password diff --git a/views/404.handlebars b/views/404.handlebars index 832f81a..3359b8c 100755 --- a/views/404.handlebars +++ b/views/404.handlebars @@ -1,5 +1,5 @@
Event not found!
+{{{__ "notFound"}}}
-It may have never existed, or if it finished more than a week ago, it's been removed from the server. Don't despair - why not create a new one? I for one would love to come to your ocarina recital.
+{{{__ "404Desc"}}}
diff --git a/views/event.handlebars b/views/event.handlebars index 88856f3..fc750ae 100755 --- a/views/event.handlebars +++ b/views/event.handlebars @@ -10,9 +10,9 @@ @@ -36,7 +36,7 @@Welcome to your event!
-Your secret editing password for this event is: {{eventData.editToken}}. It's been saved in your browser storage, and if you supplied your email, it's also been sent to you. If you didn't supply your email, you must save it somewhere safe, because it won't be shown again!
-To share your event, use the link you can see just above this message - that way your attendees won't be able to edit or delete your event!
+{{{__ "eventWelcome"}}}
+{{{__ "eventPasswordAlert"}}}
+{{{__ "eventShare"}}}
{{/if}}No attendees yet!
+{{{__ "noAttendees"}}}
{{/if}}