diff --git a/functions/i18next.config.ts b/functions/i18next.config.ts new file mode 100644 index 0000000..2407823 --- /dev/null +++ b/functions/i18next.config.ts @@ -0,0 +1,29 @@ +import {InitOptions} from "i18next"; +// import all namespaces (for the default language, only) +import emailEn from "../public/locales/en/email.json"; +import emailFr from "../public/locales/fr/email.json"; +import resourceEn from "../public/locales/en/resource.json"; +import resourceFr from "../public/locales/fr/resource.json"; + + +export const defaultNS = "email"; + +export const resources = { + en: { + email: emailEn, + resource: resourceEn, + }, + fr: { + email: emailFr, + resource: resourceFr, + }, +} as const; + + +export const i18n: InitOptions = { + // https://www.i18next.com/overview/configuration-options#logging + debug: process.env.NODE_ENV === 'development', + ns: ["resource", "email"], + resources, + defaultNS: defaultNS, +} diff --git a/functions/send-emails/invite-fr.html b/functions/send-emails/admin.html similarity index 70% rename from functions/send-emails/invite-fr.html rename to functions/send-emails/admin.html index d0f614b..07b2fbb 100644 --- a/functions/send-emails/invite-fr.html +++ b/functions/send-emails/admin.html @@ -70,7 +70,9 @@ -
Nous sommes très heureux de vous partager ce lien de vote ! Vous allez pouvoir voter avec le jugement majoritaire.
+
+ {{#i18n 'email.admin.happy' }}{{/i18n}} +
@@ -79,7 +81,7 @@ @@ -92,7 +94,7 @@
- Logo + Logo
-

Bonjour ! 🙂

+

{{#i18n 'email.hello'}}Hi, there! 🙂{{/i18n}}

@@ -105,39 +107,18 @@ -

Nous sommes très heureux de vous partager ce lien de vote ! Vous allez pouvoir voter avec le jugement majoritaire.

+

+ {{#i18n 'email.admin.happy'}}{{/i18n}} +

- - -

Vous avez été invité·e à participer à l'élection suivante :

- - - - - - - - - -
- - - - -
- Voter !
-
- - -

- Si le lien ne fonctionne pas, vous pouvez le copier et le coller dans la barre de navigation de votre navigateur. + {{#i18n 'email.admin.why'}}{{/i18n}}   - %recipient.urlVote% + %recipient.title%

@@ -145,16 +126,16 @@

- A la fin de l'Ă©lection, vous pourrez accĂ©der aux rĂ©sultats en cliquant sur ce lien : -   - %recipient.urlResult% + {{#i18n 'email.admin.linkAdmin' }}{{/i18n}} +   + %recipient.urlAdmin%

-

Bon vote,
Mieux Voter

+

{{#i18n 'email.bye'}}{{/i18n}},
{{#i18n 'common.better-vote'}}{{/i18n}}

@@ -168,10 +149,14 @@

- Besoin de plus d'information + + {{#i18n 'email.about-mj'}}{{/i18n}} +

- Vous souhaitez nous aider ? + + {{#i18n 'common.support-us'}}{{/i18n}} +

@@ -186,13 +171,15 @@
-

Vous avez été invité·e à participer à l'élection suivante

+

+ {{#i18n email.why }}{{/i18n}} +

-

Mieux Voter - app@mieuxvoter.fr

+

{{#i18n common.better-vote }}{{/i18n}} - {{ from_email_address }}

diff --git a/functions/send-emails/admin.txt b/functions/send-emails/admin.txt new file mode 100644 index 0000000..86fc8bb --- /dev/null +++ b/functions/send-emails/admin.txt @@ -0,0 +1,15 @@ +{{#i18n 'email.hello'}}{{/i18n}} + +{{#i18n 'email.admin.happy'}}{{/i18n}} + +{{#i18n 'email.admin.why'}}{{/i18n}} + +%recipient.title% + +{{#i18n 'email.admin.linkAdmin' }}{{/i18n}} + +%recipient.urlAdmin% + +{{#i18n 'email.bye'}}{{/i18n}} + +{{#i18n 'common.better-vote'}}{{/i18n}} diff --git a/functions/send-emails/index.ts b/functions/send-emails/index.ts index a69f9b3..416dc39 100644 --- a/functions/send-emails/index.ts +++ b/functions/send-emails/index.ts @@ -3,9 +3,9 @@ import {Handler} from "@netlify/functions"; import formData from 'form-data'; import Mailgun from 'mailgun.js'; import Handlebars from 'handlebars'; -import {i18n} from '../../next-i18next.config.js'; +import {i18n} from '../i18next.config'; import i18next from 'i18next'; -import {MailgunMessageData} from "mailgun.js/interfaces/Messages.js"; +import {MailgunMessageData, MessagesSendResult} from "mailgun.js/interfaces/Messages.js"; const { @@ -15,6 +15,7 @@ const { FROM_EMAIL_ADDRESS, REPLY_TO_EMAIL_ADDRESS, } = process.env; +console.log("MAILGUN URL", MAILGUN_URL) const mailgun = new Mailgun(formData); const mg = mailgun.client({ @@ -23,15 +24,9 @@ const mg = mailgun.client({ url: MAILGUN_URL }); -const txtStr = fs.readFileSync(__dirname + "/invite.txt").toString() - -i18next.init({ - fallbackLng: 'en', - ns: ['file1', 'file2'], - defaultNS: 'file1', - debug: true -}, (err, t) => { +console.log("I18N config", i18n) +i18next.init(i18n, (err, t) => { if (err) return console.log('something went wrong loading', err); t("foo"); }); @@ -39,6 +34,8 @@ i18next.init({ Handlebars.registerHelper('i18n', (str: string): string => { + console.log("I18N", str) + console.log("I18Next", i18next, i18next.t) return (i18next != undefined ? i18next.t(str) : str); } ); @@ -46,7 +43,6 @@ Handlebars.registerHelper('i18n', interface RequestPayload { recipients: {[email: string]: {[key: string]: string}}; - options: any; locale: string; action: "invite" | "admin"; } @@ -55,6 +51,7 @@ const handler: Handler = async (event) => { /** * Send a mail using Mailgun */ + if (event.httpMethod !== 'POST') { return { statusCode: 405, @@ -63,7 +60,7 @@ const handler: Handler = async (event) => { }; } - const {recipients, options, action, locale} = JSON.parse(event.body) as RequestPayload; + const {recipients, action, locale} = JSON.parse(event.body) as RequestPayload; if (!recipients) { return { @@ -79,49 +76,72 @@ const handler: Handler = async (event) => { }; } - if (!locale || !["fr", "gb"].includes(locale)) { + if (!locale || !["fr", "en"].includes(locale)) { return { statusCode: 422, body: 'Unknown locale.', }; } - // TODO setup locale + const err = await i18next.changeLanguage(locale, (err, t) => { + + if (err) return {"error": err} + }); + if (err && err["error"]) { + return {statusCode: 200, ...err["error"]} + } - const htmlTemplate = "FOO"; //fs.readFileSync(`${__dirname}/${action}.txt`).toString(); - const htmlContent = Handlebars.compile(htmlTemplate); + const templates = ["txt", "html"].map(ext => + fs.readFileSync(`${__dirname}/${action}.${ext}`).toString()); + const contents = templates.map(tpl => Handlebars.compile(tpl)({from_email_address: FROM_EMAIL_ADDRESS})); const payload: MailgunMessageData = { - // from: `${i18next.t("Mieux Voter")} `, + // from: `${i18next.t("Mieux Voter")} `, from: FROM_EMAIL_ADDRESS || '"Mieux Voter" ', to: Object.keys(recipients), - subject: i18next.t(`emails.subject-${action}`), - html: htmlContent({}), + subject: i18next.t(`email.${action}.subject`), + txt: contents[0], + html: contents[1], 'h:Reply-To': REPLY_TO_EMAIL_ADDRESS || 'app@mieuxvoter.fr', 'o:tag': action, 'o:require-tls': true, 'o:dkim': true, 'recipient-variables': JSON.stringify(recipients), }; - console.log(payload) try { - const res = await mg.messages.create(MAILGUN_DOMAIN, payload) + const res: MessagesSendResult = await mg.messages.create(MAILGUN_DOMAIN, payload) + + if (res.status != 200) { + return { + statusCode: res.status, + body: JSON.stringify( + { + "message": res.message, + "details": res.details, + } + ) + } + + } return { statusCode: 200, body: JSON.stringify( { - ...res, - "status": `${recipients.length} emails were sent.`, + "message": res.message, + "details": res.details, + "status": "200", + "results": `${Object.keys(recipients).length} emails were sent.`, } ) } - } catch { + } catch (error) { return { statusCode: 422, body: JSON.stringify( { - "status": "can not send the message" + "status": "423", + ...error } ) } diff --git a/functions/send-emails/invite-en.html b/functions/send-emails/invite-en.html deleted file mode 100644 index a399098..0000000 --- a/functions/send-emails/invite-en.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - - - - - - - -
- {{#i18n 'email.happy' }}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}} -
- - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - Logo - -
-
- - - - -
-

{{#i18n 'email.hello'}}Hi, there! 🙂{{/i18n}}

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
-

- {{#i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}} -

-
-

- {{#i18n 'email.why'}}This email was sent to you because your email address was entered to participate in the vote on the subject:{{/i18n}} -   - {{title}} -

-
- - - - -
- - - - -
- - {{#i18n 'common.vote' }}Vote!{{/i18n}}
-
-
-

- {{#i18n 'email.copyLink' }}If that doesn't work, copy and paste the following link into your browser:{{/i18n}} -   - %recipient.urlVote% -

-
-

- {{#i18n 'email.linkResult' }}The results will be available with the following link when the vote is finished:{{/i18n}} -   - %recipient.urlResult% -

-
-

{{#i18n 'email.bye'}}Good vote{{/i18n}},
{{#i18n 'common.mieuxvoter'}}Mieux Voter{{/i18n}}

-
-
- - - - - -
-

- - {{#i18n 'email.aboutjm'}}Need any further information?{{/i18n}} - -

-

- - {{#i18n 'common.helpus'}}Do you want to help us?{{/i18n}} - -

-
-
- - -
- - - - - - - -
-

- {{#i18n email.why }}You received this email because someone invited you to vote.{{/i18n}} -

-
-

{{#i18n mieuxvoter }}Mieux Voter{{/i18n}} - app@mieuxvoter.fr

-
-
- - diff --git a/functions/send-emails/invite-en.txt b/functions/send-emails/invite-en.txt deleted file mode 100644 index f4b8f60..0000000 --- a/functions/send-emails/invite-en.txt +++ /dev/null @@ -1,19 +0,0 @@ -{{i18n 'email.hello'}}Hi there! 🙂{{i18n}} - -{{i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{i18n}} - -{{i18n 'email.why'}}This email was sent to you because your email was filled out to participate in the vote on the subject:{{i18n}} - -{{ title }} - -{{i18n 'email.linkVote' }}The link for the vote is as follows:{{i18n}} - -%recipient.urlVote% - -{{i18n 'email.linkResult' }}The link that will give you the results when they are available is as follows:{{i18n}} - -%recipient.urlResult% - -{{i18n 'email.bye'}}Good vote{{i18n}} - -{{i18n 'common.mieuxvoter'}}Mieux Voter{{i18n}} diff --git a/functions/send-emails/invite-fr.txt b/functions/send-emails/invite-fr.txt deleted file mode 100644 index 9ec0f3e..0000000 --- a/functions/send-emails/invite-fr.txt +++ /dev/null @@ -1,17 +0,0 @@ -Bonjour ! 🙂 - -Vous avez été invité·e à participer à l'élection suivante : - -{{ title }} - -Le lien pour voter est le suivant : - -%recipient.urlVote% - -A la fin de l'élection, vous pourrez accéder aux résultats en cliquant sur ce lien : - -%recipient.urlResult% - -Bon vote ! 🤗 - -Mieux Voter diff --git a/functions/send-emails/invite.html b/functions/send-emails/invite.html index a399098..bd4db4c 100644 --- a/functions/send-emails/invite.html +++ b/functions/send-emails/invite.html @@ -71,7 +71,7 @@
- {{#i18n 'email.happy' }}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}} + {{#i18n 'email.invite.happy' }}{{/i18n}}
@@ -81,7 +81,7 @@ @@ -108,7 +108,7 @@ @@ -116,9 +116,9 @@ @@ -132,7 +132,7 @@ + {{#i18n 'resource:common.vote' }}Vote!{{/i18n}}
- Logo + Logo

- {{#i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}} + {{#i18n 'email.invite.happy'}}{{/i18n}}

- {{#i18n 'email.why'}}This email was sent to you because your email address was entered to participate in the vote on the subject:{{/i18n}} + {{#i18n 'email.invite.why'}}{{/i18n}}   - {{title}} + %recipient.title%

- {{#i18n 'common.vote' }}Vote!{{/i18n}}
@@ -144,9 +144,14 @@

- {{#i18n 'email.copyLink' }}If that doesn't work, copy and paste the following link into your browser:{{/i18n}} + {{#i18n 'email.copyLink' }}{{/i18n}}   - %recipient.urlVote% + + %recipient.urlVote% +

@@ -154,16 +159,21 @@

- {{#i18n 'email.linkResult' }}The results will be available with the following link when the vote is finished:{{/i18n}} + {{#i18n 'email.invite.linkResult' }}{{/i18n}}   - %recipient.urlResult% + + %recipient.urlResult% +

-

{{#i18n 'email.bye'}}Good vote{{/i18n}},
{{#i18n 'common.mieuxvoter'}}Mieux Voter{{/i18n}}

+

{{#i18n 'email.bye'}}{{/i18n}}
{{#i18n 'resource:common.better-vote'}}{{/i18n}}

@@ -177,13 +187,13 @@

- - {{#i18n 'email.aboutjm'}}Need any further information?{{/i18n}} + + {{#i18n 'email.about-mj'}}{{/i18n}}

- - {{#i18n 'common.helpus'}}Do you want to help us?{{/i18n}} + + {{#i18n 'resource:common.support-us'}}{{/i18n}}

@@ -200,14 +210,14 @@

- {{#i18n email.why }}You received this email because someone invited you to vote.{{/i18n}} + {{#i18n email.why }}{{/i18n}}

-

{{#i18n mieuxvoter }}Mieux Voter{{/i18n}} - app@mieuxvoter.fr

+

{{#i18n resource:common.better-vote }}{{/i18n}} - {{ from_email_address }}

diff --git a/functions/send-emails/invite.txt b/functions/send-emails/invite.txt index 92a8522..9e546cb 100644 --- a/functions/send-emails/invite.txt +++ b/functions/send-emails/invite.txt @@ -1,19 +1,19 @@ -{{#i18n 'email.hello'}}Hi there! 🙂{{/i18n}} +{{#i18n 'email.hello'}}{{/i18n}} -{{#i18n 'email.happy'}}We are happy to send you this email! You will be able to vote using majority judgment.{{/i18n}} +{{#i18n 'email.invite.happy'}}{{/i18n}} -{{#i18n 'email.why'}}This email was sent to you because your email was filled out to participate in the vote on the subject:{{/i18n}} +{{#i18n 'email.invite.why'}}{{/i18n}} %recipient.title% -{{#i18n 'email.linkVote' }}The link for the vote is as follows:{{/i18n}} +{{#i18n 'email.invite.linkVote' }}{{/i18n}} %recipient.urlVote% -{{#i18n 'email.linkResult' }}The link that will give you the results when they are available is as follows:{{/i18n}} +{{#i18n 'email.invite.linkResult' }}{{/i18n}} %recipient.urlResult% -{{#i18n 'email.bye'}}Good vote{{/i18n}} +{{#i18n 'email.bye'}}{{/i18n}} -{{#i18n 'common.mieuxvoter'}}Mieux Voter{{/i18n}} +{{#i18n 'common.better-vote'}}{{/i18n}} diff --git a/functions/test/test.ts b/functions/test/test.ts new file mode 100644 index 0000000..0ed6c84 --- /dev/null +++ b/functions/test/test.ts @@ -0,0 +1,16 @@ +console.log(__dirname) + +const send = async (event) => { + /** + * Send a mail using Mailgun + */ + return { + statusCode: 405, + body: __dirname, + headers: {Allow: 'POST'}, + }; + +}; + +exports.handler = send + diff --git a/netlify.toml b/netlify.toml index 4423fc7..9209742 100644 --- a/netlify.toml +++ b/netlify.toml @@ -6,3 +6,6 @@ [dev] command = "npm run dev" +[functions] + included_files = ["functions/**"] + diff --git a/package-lock.json b/package-lock.json index d2462e5..054ab04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "react-i18next": "^12.0.0", "reactstrap": "^9.1.4", "sass": "^1.32.13", + "ts-node": "^10.9.1", "typescript": "^4.8.4" }, "devDependencies": { @@ -405,6 +406,26 @@ "node": ">=6.9.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@dnd-kit/accessibility": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz", @@ -630,7 +651,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -647,8 +667,7 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "peer": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.17", @@ -1037,6 +1056,26 @@ "tslib": "^2.4.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -1054,8 +1093,7 @@ "node_modules/@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "dev": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -1214,6 +1252,14 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1261,6 +1307,11 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1669,6 +1720,11 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1791,6 +1847,14 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3417,6 +3481,11 @@ "form-data": "^4.0.0" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4516,6 +4585,48 @@ "node": ">=8.0" } }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -4671,6 +4782,11 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -4740,6 +4856,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -5018,6 +5142,25 @@ "to-fast-properties": "^2.0.0" } }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, "@dnd-kit/accessibility": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz", @@ -5177,8 +5320,7 @@ "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "peer": true + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" }, "@jridgewell/set-array": { "version": "1.1.2", @@ -5189,8 +5331,7 @@ "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "peer": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { "version": "0.3.17", @@ -5437,6 +5578,26 @@ "tslib": "^2.4.0" } }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -5454,8 +5615,7 @@ "@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "dev": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "@types/prop-types": { "version": "15.7.5", @@ -5553,6 +5713,11 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "requires": {} }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -5587,6 +5752,11 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5881,6 +6051,11 @@ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", "integrity": "sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA==" }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5964,6 +6139,11 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7138,6 +7318,11 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7856,6 +8041,26 @@ "is-number": "^7.0.0" } }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, "tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -7963,6 +8168,11 @@ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", "requires": {} }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, "void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -8017,6 +8227,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 25deb61..b69d3c7 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "react-i18next": "^12.0.0", "reactstrap": "^9.1.4", "sass": "^1.32.13", + "ts-node": "^10.9.1", "typescript": "^4.8.4" }, "devDependencies": { diff --git a/public/locales/en/email.json b/public/locales/en/email.json new file mode 100644 index 0000000..e21b50b --- /dev/null +++ b/public/locales/en/email.json @@ -0,0 +1,16 @@ +{ + "email.hello": "Hi there! 🙂", + "email.invite.subject": "You are invited to vote", + "email.invite.happy": "We are happy to send you this email! You will be able to vote using majority judgment.", + "email.invite.why": "This email was sent to you because your email was filled out to participate in the vote on the subject:", + "email.invite.linkVote": "The link for the vote is as follows:", + "email.invite.linkResult": "The link that will give you the results when they are available is as follows:", + "email.invite.bye": "Good bye", + "email.admin.subject": "Manage your election", + "email.admin.happy": "We are happy to send you this email! You have created a vote using majority judgment.", + "email.admin.why": "This email was sent to you since you have asked us to send the admin link", + "email.admin.linkAdmin": "The below admin link allows you to manage your election.", + "email.bye": "Good bye", + "email.copyLink": "If that doesn't work, copy and paste the following link into your browser:", + "email.about-mj": "Need any further information?" +} diff --git a/public/locales/en/resource.json b/public/locales/en/resource.json index fce6e8c..5ecbbde 100644 --- a/public/locales/en/resource.json +++ b/public/locales/en/resource.json @@ -1,98 +1,100 @@ { - "home.motto": "Simple and free", - "home.slogan": "Organize a vote with majority judgment", - "logo.alt": "Logo of Better Vote", - "home.writeQuestion": "Write here your question or describe your vote.", - "home.start": "Start a vote", - "home.noAds": "No advertising or ad cookies", - "home.advantage-1-title": "Simple", - "home.advantage-1-desc": "Create a vote in less than 1 minute!", - "home.advantage-2-title": "Free", - "home.advantage-2-desc": "Send invites without any limitations!", - "home.advantage-3-title": "Respecting your privacy", - "home.advantage-3-desc": "No personal data is recorded", - "home.experience-title": "A democratic and intuitive voting experience", - "home.experience-1-title": "Express your full opinion.", - "home.experience-1-desc": "With majority judgment, each candidate is evaluated on a grid of mentions. Strategic voting has no use anymore.", - "home.experience-2-title": "Get the best possible consensus.", - "home.experience-2-desc": "The merit profile provides an accurate picture of the voters' opinions. The winner of the vote is the one with the best majority rating.", - "home.experience-call-to-action": "Find out about the majority judgment", - "home.alt-icon-ballot-box": "icon of a ballot box", - "home.alt-icon-envelop": "icon of an envelop", - "home.alt-icon-respect": "icon of hands holding each other", - "home.alt-icon-ballot": "icon of a ballot", - "menu.majority-judgment": "Majority judgment", - "menu.whoarewe": "Who are we?", - "menu.faq": "FAQ", - "menu.news": "News", - "menu.contact-us": "Contact us", - "common.error": "Oh no... An error has occured...", - "common.share": "Share the application Better Vote", - "common.share-short": "Share the application", - "common.save": "Save", - "common.back-homepage": "Return to home page", - "common.support-us": "Support us", - "common.thumbnail": "Thumbnail", - "common.name": "Name", - "common.description": "Description", - "common.cancel": "Cancel", - "common.grades": "Grades", - "common.invites": "invites", - "common.days": "days", - "common.send": "Send", - "common.the-vote": "The vote", - "common.the-params": "The parameters", - "error.help": "Ask for our help", - "error.at-least-2-candidates": "At least two candidates are required.", - "error.no-title": "Please add a title to your election.", - "grades.very-good": "Very good", - "grades.good": "Good", - "grades.passable": "Passable", - "grades.inadequate": "Inadequate", - "grades.mediocre": "Mediocre", - "admin.date-limit": "Set a deadline for voting", - "admin.step-candidate": "Candidates", - "admin.step-params": "Parameters", - "admin.step-confirm": "Confirm", - "admin.add-candidates": "Add the candidates.", - "admin.add-candidate": "Add a candidate", - "admin.add-candidate-desc": "Add a picture, a name, and a description of the candidate.", - "admin.add-grade": "Add a grade", - "admin.add-grade-desc": "The name of the grade must be unique.", - "admin.grade-name-placeholder": "Add the name of the grade", - "admin.candidate-confirm-del": "You want to remove a candidate", - "admin.candidate-confirm-back": "No, I keep it", - "admin.candidate-confirm-ok": "Delete", - "admin.candidate-name-placeholder": "Add the name or the title of the candidate.", - "admin.candidate-desc-placeholder": "Add the description of the candidate.", - "admin.candidates-submit": "Validate the candidates", - "admin.candidates-back-step": "Back to candidates", - "admin.modal-title": "Managing an election", - "admin.modal-desc": "This link allows you to modify your election. Keep it carefully, or fill out this form to receive a copy by mail.", - "admin.modal-disclaimer": "We do not store any mail. Thus, we will not send you any advertising content.", - "admin.modal-email-placeholder": "Your mail address", - "admin.params-submit": "Validate the parameters", - "admin.params-title": "Your parameters", - "admin.access-results": "Immediate access to the results", - "admin.access-results-desc": "No one can access to the results as long as the vote is not closed.", - "admin.limit-duration": "Limit the length of the vote", - "admin.limit-duration-desc": "", - "admin.photo": "Picture", - "admin.optional": "optional", - "admin.photo-import": "Import a picture", - "admin.photo-type": "Supported type:", - "admin.grades-desc": "You can choose to customize the name and the number of mentions. If in doubt, leave the grades as is.", - "admin.ending-in": "In", - "admin.until": "Until", - "admin.private-title": "Private vote", - "admin.private-desc": "Only participants who received an invite by email will be able to vote", - "admin.private-tip": "You can copy-paste a list of emails from a spreadsheet.", - "admin.private-placeholder": "Add here the emails of the participants.", - "admin.confirm-question": "Question of your vote", - "admin.confirm-candidates": "Candidates", - "admin.confirm-submit": "Start the vote", - "admin.confirm-title": "Confirm your vote", - "admin.success-election": "The vote has been created with success!", - "admin.success-emails": "The voting link has been sent by emails to the participants.", - "admin.go-to-admin": "Manage the vote" + "home.motto": "Simple and free", + "home.slogan": "Organize a vote with majority judgment", + "logo.alt": "Logo of Better Vote", + "home.writeQuestion": "Write here your question or describe your vote.", + "home.start": "Start a vote", + "home.noAds": "No advertising or ad cookies", + "home.advantage-1-title": "Simple", + "home.advantage-1-desc": "Create a vote in less than 1 minute!", + "home.advantage-2-title": "Free", + "home.advantage-2-desc": "Send invites without any limitations!", + "home.advantage-3-title": "Respecting your privacy", + "home.advantage-3-desc": "No personal data is recorded", + "home.experience-title": "A democratic and intuitive voting experience", + "home.experience-1-title": "Express your full opinion.", + "home.experience-1-desc": "With majority judgment, each candidate is evaluated on a grid of mentions. Strategic voting has no use anymore.", + "home.experience-2-title": "Get the best possible consensus.", + "home.experience-2-desc": "The merit profile provides an accurate picture of the voters' opinions. The winner of the vote is the one with the best majority rating.", + "home.experience-call-to-action": "Find out about the majority judgment", + "home.alt-icon-ballot-box": "icon of a ballot box", + "home.alt-icon-envelop": "icon of an envelop", + "home.alt-icon-respect": "icon of hands holding each other", + "home.alt-icon-ballot": "icon of a ballot", + "menu.majority-judgment": "Majority judgment", + "menu.whoarewe": "Who are we?", + "menu.faq": "FAQ", + "menu.news": "News", + "menu.contact-us": "Contact us", + "common.error": "Oh no... An error has occured...", + "common.better-vote": "Better Vote", + "common.share": "Share the application Better Vote", + "common.share-short": "Share the application", + "common.save": "Save", + "common.back-homepage": "Return to home page", + "common.support-us": "Support us", + "common.thumbnail": "Thumbnail", + "common.name": "Name", + "common.description": "Description", + "common.cancel": "Cancel", + "common.grades": "Grades", + "common.invites": "invites", + "common.days": "days", + "common.send": "Send", + "common.vote": "Vote", + "common.the-vote": "The vote", + "common.the-params": "The parameters", + "error.help": "Ask for our help", + "error.at-least-2-candidates": "At least two candidates are required.", + "error.no-title": "Please add a title to your election.", + "grades.very-good": "Very good", + "grades.good": "Good", + "grades.passable": "Passable", + "grades.inadequate": "Inadequate", + "grades.mediocre": "Mediocre", + "admin.date-limit": "Set a deadline for voting", + "admin.step-candidate": "Candidates", + "admin.step-params": "Parameters", + "admin.step-confirm": "Confirm", + "admin.add-candidates": "Add the candidates.", + "admin.add-candidate": "Add a candidate", + "admin.add-candidate-desc": "Add a picture, a name, and a description of the candidate.", + "admin.add-grade": "Add a grade", + "admin.add-grade-desc": "The name of the grade must be unique.", + "admin.grade-name-placeholder": "Add the name of the grade", + "admin.candidate-confirm-del": "You want to remove a candidate", + "admin.candidate-confirm-back": "No, I keep it", + "admin.candidate-confirm-ok": "Delete", + "admin.candidate-name-placeholder": "Add the name or the title of the candidate.", + "admin.candidate-desc-placeholder": "Add the description of the candidate.", + "admin.candidates-submit": "Validate the candidates", + "admin.candidates-back-step": "Back to candidates", + "admin.modal-title": "Managing an election", + "admin.modal-desc": "This link allows you to modify your election. Keep it carefully, or fill out this form to receive a copy by mail.", + "admin.modal-disclaimer": "We do not store any mail. Thus, we will not send you any advertising content.", + "admin.modal-email-placeholder": "Your mail address", + "admin.params-submit": "Validate the parameters", + "admin.params-title": "Your parameters", + "admin.access-results": "Immediate access to the results", + "admin.access-results-desc": "No one can access to the results as long as the vote is not closed.", + "admin.limit-duration": "Limit the length of the vote", + "admin.limit-duration-desc": "", + "admin.photo": "Picture", + "admin.optional": "optional", + "admin.photo-import": "Import a picture", + "admin.photo-type": "Supported type:", + "admin.grades-desc": "You can choose to customize the name and the number of mentions. If in doubt, leave the grades as is.", + "admin.ending-in": "In", + "admin.until": "Until", + "admin.private-title": "Private vote", + "admin.private-desc": "Only participants who received an invite by email will be able to vote", + "admin.private-tip": "You can copy-paste a list of emails from a spreadsheet.", + "admin.private-placeholder": "Add here the emails of the participants.", + "admin.confirm-question": "Question of your vote", + "admin.confirm-candidates": "Candidates", + "admin.confirm-submit": "Start the vote", + "admin.confirm-title": "Confirm your vote", + "admin.success-election": "The vote has been created with success!", + "admin.success-emails": "The voting link has been sent by emails to the participants.", + "admin.go-to-admin": "Manage the vote" } diff --git a/public/locales/fr/email.json b/public/locales/fr/email.json new file mode 100644 index 0000000..b99748f --- /dev/null +++ b/public/locales/fr/email.json @@ -0,0 +1,15 @@ +{ + "email.hello": "Bonjour ! 🙂", + "email.invite.subject": "Vous participez au vote en cours", + "email.invite.happy": "Nous sommes très heureux de vous envoyer ce courriel ! Vous allez pouvoir voter en utilisant le jugement majoritaire.", + "email.invite.why": "Ce courriel vous a été envoyé, car l'organisateur du vote vous a ajoutés dans la liste des participants. Le vote porte sur :", + "email.invite.linkVote": "Le lien pour voter est ci-dessous", + "email.invite.linkResult": "Le lien pour voir les résultats est ci-dessous. Conservez le bien !", + "email.admin.subject": "Gérez votre élection", + "email.admin.happy": "Nous sommes très heureux de vous envoyer ce courriel ! Vous venez de créer une élection avec le jugement majoritaire.", + "email.admin.why": "Vous recevez ce courriel parce que vous nous avez chargé de vous transmettre le lien d'administration. Gardez le précieusement : un autre lien ne pourra leien ne pourra pas vous être partagé.", + "email.admin.linkAdmin": "Ce lien ci-dessous vous permet de gérer votre élection.", + "email.bye": "Au revoir !", + "email.copyLink": "Si cela ne fonctionne pas, copiez-collez le lien suivant dans votre navigateur :", + "email.about-mj": "Besoin de plus d'information ?" +} diff --git a/public/locales/fr/resource.json b/public/locales/fr/resource.json index 3a29259..c4d0771 100644 --- a/public/locales/fr/resource.json +++ b/public/locales/fr/resource.json @@ -1,98 +1,100 @@ { - "home.motto": "Simple et gratuit", - "home.slogan": "Organisez un vote avec le jugement majoritaire", - "logo.alt": "Logo de Mieux Voter", - "home.writeQuestion": "Posez la question de votre vote ici.", - "home.start": "C'est parti", - "home.noAds": "Pas de publicités, ni de cookies publicitaires", - "home.advantage-1-title": "Simple", - "home.advantage-1-desc": "Créez un vote en moins d’une minute.", - "home.advantage-2-title": "Gratuit", - "home.advantage-2-desc": "Envoyez des invitations par courriel sans limite d'envoi.", - "home.advantage-3-title": "Respect de votre vie privée", - "home.advantage-3-desc": "Aucune donnée personnelle n'est enregistrée", - "home.experience-title": "Une expérience de vote démocratique et intuitive", - "home.experience-1-title": "Exprimez toute votre opinion.", - "home.experience-1-desc": "Au jugement majoritaire, chaque candidat est évalué sur une grille de mention. Vous n'aurez plus besoin de faire un vote stratégique.", - "home.experience-2-title": "Obtenez le meilleur consensus.", - "home.experience-2-desc": "Le profil des mérites dresse un panorama précis de l’opinion des électeurs. Le gagnant du vote est celui qui est la meilleure mention majoritaire.", - "home.experience-call-to-action": "Découvrez le jugement majoritaire", - "home.alt-icon-ballot-box": "icone d'urne", - "home.alt-icon-envelop": "icone d'enveloppe", - "home.alt-icon-respect": "icone de mains qui se serrent", - "home.alt-icon-ballot": "icone d'un bulletin de vote", - "menu.majority-judgment": "Jugement majoritaire", - "menu.whoarewe": "Qui sommes-nous ?", - "menu.faq": "FAQ", - "menu.news": "Actualités", - "menu.contact-us": "Nous contacter", - "common.error": "Oh non ! Une erreur s'est produite...", - "common.share": "Partagez l'application Mieux voter", - "common.share-short": "Partagez l'application", - "common.back-homepage": "Revenir sur la page d'accueil", - "common.support-us": "Soutenez nous", - "common.save": "Sauvegarder", - "common.thumbnail": "Image miniature", - "common.name": "Nom", - "common.description": "Description", - "common.cancel": "Annuler", - "common.grades": "Mentions", - "common.days": "jours", - "common.invites": "invitations", - "common.send": "Envoyer", - "common.the-vote": "Le vote", - "common.the-params": "Les paramètres", - "error.help": "Besoin d'aide ?", - "error.at-least-2-candidates": "Ajoutez au moins deux candidats.", - "error.no-title": "Ajoutez un titre à l'élection.", - "grades.very-good": "Très bien", - "grades.good": "Bien", - "grades.passable": "Passable", - "grades.inadequate": "Insuffisant", - "grades.mediocre": "Médiocre", - "admin.date-limit": "Fixer une date limite pour le vote", - "admin.step-candidate": "Les candidats", - "admin.step-params": "Paramètres du vote", - "admin.step-confirm": "Confirmation", - "admin.add-candidates": "Ajouter les candidats.", - "admin.add-candidate": "Ajouter un candidat", - "admin.candidate-name-placeholder": "Ajouter le nom ou le titre du candidat.", - "admin.candidate-desc-placeholder": "Ajouter la description du candidat.", - "admin.add-candidate-desc": "Ajouter une photo, le nom et une description au candidat.", - "admin.add-grade": "Ajouter une mention", - "admin.add-grade-desc": "Le nom de la mention doit être unique.", - "admin.grade-name-placeholder": "Ajoutez le nom de la mention.", - "admin.candidate-confirm-del": "Vous souhaitez supprimer un candidat", - "admin.candidate-confirm-back": "Non, je le garde", - "admin.candidate-confirm-ok": "Supprimer", - "admin.candidates-submit": "Valider les candidats", - "admin.candidates-back-step": "Retour aux candidats", - "admin.params-submit": "Valider les paramètres", - "admin.params-title": "Vos paramètres", - "admin.access-results": "Accès immédiat aux résultats", - "admin.access-results-desc": "Personne ne pourra accéder aux résultats tant que le vote n'est pas clôturé.", - "admin.limit-duration": "Limiter la durée du vote", - "admin.limit-duration-desc": "", - "admin.modal-title": "Administration du vote", - "admin.modal-desc": "Ce lien vous permet de modifier votre vote. Conservez le précieusement, ou remplissez votre adresse email ci-dessous pour en recevoir une copie", - "admin.modal-disclaimer": "Nous ne stockons aucun email. Nous ne vous enverrons donc aucun contenu publicitaire.", - "admin.modal-email-placeholder": "Votre adresse email", - "admin.photo": "Photo", - "admin.optional": "facultatif", - "admin.photo-import": "Importer une photo", - "admin.photo-type": "Format supporté :", - "admin.grades-desc": "Vous pouvez choisir de personaliser le nom et le nombre de mentions. En cas de doute, gardez les mentions par défaut.", - "admin.ending-in": "Dans", - "admin.until": "Jusqu'au", - "admin.private-title": "Vote privé", - "admin.private-desc": "Uniquement les personnes invités par mail pourront participé au vote", - "admin.private-tip": "Vous pouvez copier-coller une liste d'emails depuis un tableur.", - "admin.private-placeholder": "Ajoutez ici les emails des participants.", - "admin.confirm-question": "Question de votre vote", - "admin.confirm-candidates": "Candidats", - "admin.confirm-submit": "Démarrer le vote", - "admin.confirm-title": "Confirmer votre vote", - "admin.success-election": "Le vote a été créé avec succès", - "admin.success-emails": "Le lien du vote a été envoyé par courriel aux participants.", - "admin.go-to-admin": "Administrez le vote" + "home.motto": "Simple et gratuit", + "home.slogan": "Organisez un vote avec le jugement majoritaire", + "logo.alt": "Logo de Mieux Voter", + "home.writeQuestion": "Posez la question de votre vote ici.", + "home.start": "C'est parti", + "home.noAds": "Pas de publicités, ni de cookies publicitaires", + "home.advantage-1-title": "Simple", + "home.advantage-1-desc": "Créez un vote en moins d’une minute.", + "home.advantage-2-title": "Gratuit", + "home.advantage-2-desc": "Envoyez des invitations par courriel sans limite d'envoi.", + "home.advantage-3-title": "Respect de votre vie privée", + "home.advantage-3-desc": "Aucune donnée personnelle n'est enregistrée", + "home.experience-title": "Une expérience de vote démocratique et intuitive", + "home.experience-1-title": "Exprimez toute votre opinion.", + "home.experience-1-desc": "Au jugement majoritaire, chaque candidat est évalué sur une grille de mention. Vous n'aurez plus besoin de faire un vote stratégique.", + "home.experience-2-title": "Obtenez le meilleur consensus.", + "home.experience-2-desc": "Le profil des mérites dresse un panorama précis de l’opinion des électeurs. Le gagnant du vote est celui qui est la meilleure mention majoritaire.", + "home.experience-call-to-action": "Découvrez le jugement majoritaire", + "home.alt-icon-ballot-box": "icone d'urne", + "home.alt-icon-envelop": "icone d'enveloppe", + "home.alt-icon-respect": "icone de mains qui se serrent", + "home.alt-icon-ballot": "icone d'un bulletin de vote", + "menu.majority-judgment": "Jugement majoritaire", + "menu.whoarewe": "Qui sommes-nous ?", + "menu.faq": "FAQ", + "menu.news": "Actualités", + "menu.contact-us": "Nous contacter", + "common.error": "Oh non ! Une erreur s'est produite...", + "common.better-vote": "Mieux Voter", + "common.share": "Partagez l'application Mieux voter", + "common.share-short": "Partagez l'application", + "common.back-homepage": "Revenir sur la page d'accueil", + "common.support-us": "Soutenez nous", + "common.save": "Sauvegarder", + "common.thumbnail": "Image miniature", + "common.name": "Nom", + "common.description": "Description", + "common.cancel": "Annuler", + "common.grades": "Mentions", + "common.days": "jours", + "common.invites": "invitations", + "common.send": "Envoyer", + "common.the-vote": "Le vote", + "common.the-params": "Les paramètres", + "common.vote": "Voter", + "error.help": "Besoin d'aide ?", + "error.at-least-2-candidates": "Ajoutez au moins deux candidats.", + "error.no-title": "Ajoutez un titre à l'élection.", + "grades.very-good": "Très bien", + "grades.good": "Bien", + "grades.passable": "Passable", + "grades.inadequate": "Insuffisant", + "grades.mediocre": "Médiocre", + "admin.date-limit": "Fixer une date limite pour le vote", + "admin.step-candidate": "Les candidats", + "admin.step-params": "Paramètres du vote", + "admin.step-confirm": "Confirmation", + "admin.add-candidates": "Ajouter les candidats.", + "admin.add-candidate": "Ajouter un candidat", + "admin.candidate-name-placeholder": "Ajouter le nom ou le titre du candidat.", + "admin.candidate-desc-placeholder": "Ajouter la description du candidat.", + "admin.add-candidate-desc": "Ajouter une photo, le nom et une description au candidat.", + "admin.add-grade": "Ajouter une mention", + "admin.add-grade-desc": "Le nom de la mention doit être unique.", + "admin.grade-name-placeholder": "Ajoutez le nom de la mention.", + "admin.candidate-confirm-del": "Vous souhaitez supprimer un candidat", + "admin.candidate-confirm-back": "Non, je le garde", + "admin.candidate-confirm-ok": "Supprimer", + "admin.candidates-submit": "Valider les candidats", + "admin.candidates-back-step": "Retour aux candidats", + "admin.params-submit": "Valider les paramètres", + "admin.params-title": "Vos paramètres", + "admin.access-results": "Accès immédiat aux résultats", + "admin.access-results-desc": "Personne ne pourra accéder aux résultats tant que le vote n'est pas clôturé.", + "admin.limit-duration": "Limiter la durée du vote", + "admin.limit-duration-desc": "", + "admin.modal-title": "Administration du vote", + "admin.modal-desc": "Ce lien vous permet de modifier votre vote. Conservez le précieusement, ou remplissez votre adresse email ci-dessous pour en recevoir une copie", + "admin.modal-disclaimer": "Nous ne stockons aucun email. Nous ne vous enverrons donc aucun contenu publicitaire.", + "admin.modal-email-placeholder": "Votre adresse email", + "admin.photo": "Photo", + "admin.optional": "facultatif", + "admin.photo-import": "Importer une photo", + "admin.photo-type": "Format supporté :", + "admin.grades-desc": "Vous pouvez choisir de personaliser le nom et le nombre de mentions. En cas de doute, gardez les mentions par défaut.", + "admin.ending-in": "Dans", + "admin.until": "Jusqu'au", + "admin.private-title": "Vote privé", + "admin.private-desc": "Uniquement les personnes invités par mail pourront participé au vote", + "admin.private-tip": "Vous pouvez copier-coller une liste d'emails depuis un tableur.", + "admin.private-placeholder": "Ajoutez ici les emails des participants.", + "admin.confirm-question": "Question de votre vote", + "admin.confirm-candidates": "Candidats", + "admin.confirm-submit": "Démarrer le vote", + "admin.confirm-title": "Confirmer votre vote", + "admin.success-election": "Le vote a été créé avec succès", + "admin.success-emails": "Le lien du vote a été envoyé par courriel aux participants.", + "admin.go-to-admin": "Administrez le vote" } diff --git a/tests/admin-en.json b/tests/admin-en.json new file mode 100644 index 0000000..e2b0b74 --- /dev/null +++ b/tests/admin-en.json @@ -0,0 +1,14 @@ +{ + "recipients": { + "app@mieuxvoter.fr": { + "title": "Test", + "urlAdmin": "foo" + }, + "tech@mieuxvoter.fr": { + "title": "Test", + "urlAdmin": "foo" + } + }, + "action": "admin", + "locale": "en" +} diff --git a/tests/admin-fr.json b/tests/admin-fr.json new file mode 100644 index 0000000..a2354d3 --- /dev/null +++ b/tests/admin-fr.json @@ -0,0 +1,14 @@ +{ + "recipients": { + "app@mieuxvoter.fr": { + "title": "Test", + "urlAdmin": "foo" + }, + "tech@mieuxvoter.fr": { + "title": "Test", + "urlAdmin": "foo" + } + }, + "action": "admin", + "locale": "fr" +} diff --git a/tests/invite-en.json b/tests/invite-en.json new file mode 100644 index 0000000..910d05a --- /dev/null +++ b/tests/invite-en.json @@ -0,0 +1,16 @@ +{ + "recipients": { + "app@mieuxvoter.fr": { + "title": "Test", + "urlVote": "foo", + "urlResult": "foo" + }, + "tech@mieuxvoter.fr": { + "title": "Test", + "urlVote": "foo", + "urlResult": "foo" + } + }, + "action": "invite", + "locale": "en" +} diff --git a/tests/invite-fr.json b/tests/invite-fr.json new file mode 100644 index 0000000..6da9baf --- /dev/null +++ b/tests/invite-fr.json @@ -0,0 +1,16 @@ +{ + "recipients": { + "app@mieuxvoter.fr": { + "title": "Test", + "urlVote": "foo", + "urlResult": "foo" + }, + "tech@mieuxvoter.fr": { + "title": "Test", + "urlVote": "foo", + "urlResult": "foo" + } + }, + "action": "invite", + "locale": "fr" +} diff --git a/tests/netlify-send-emails.sh b/tests/netlify-send-emails.sh index 13254b4..f1d89d0 100755 --- a/tests/netlify-send-emails.sh +++ b/tests/netlify-send-emails.sh @@ -15,5 +15,20 @@ else fi -netlify functions:invoke --port 9999 send-emails --payload '{"recipients" -: {"pierrelouisguhur@gmail.com": {"title": "Test", "adminUrl": "foo"}}, "action": "invite", "locale": "gb"}' +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +send_emails () { + res=$(netlify functions:invoke --port 9999 send-emails --payload "$(cat $1)") + echo "$res" + # status=$(echo "$res" | head -n 1 | cut -d ',' -f 1 | cut -d ':' -f 2) + status=$(echo "$res" | jq '.["status"]') + if [ "$status" != '"200"' ]; then + echo "Issue with $1"; + exit 2 + fi +} + +send_emails $SCRIPT_DIR/invite-en.json # && \ +#send_emails $SCRIPT_DIR/invite-fr.json && \ +#send_emails $SCRIPT_DIR/admin-en.json && \ +#send_emails $SCRIPT_DIR/admin-fr.json