- Personal Access Tokens act like your password to allow automated
- scripts to access the Editor API. Create a token for each script that
- needs access.
+ {this.props.t('APIKeyForm.Summary')}
-
Create new token
+
{this.props.t('APIKeyForm.CreateToken')}
- {keyWithToken && (
-
-
- Your new access token
-
-
- Make sure to copy your new personal access token now. You won’t
- be able to see it again!
-
- Use your GitHub or Google account to log into the p5.js Web Editor.
+ {/* eslint-disable-next-line react/prop-types */}
+ {props.t('AccountView.SocialLoginDescription')}
- p5.js Web Editor | Account Settings
+ {this.props.t('AccountView.Title')}
-
Account Settings
+
{this.props.t('AccountView.Settings')}
{accessTokensUIEnabled &&
-
Account
- {accessTokensUIEnabled &&
Access Tokens
}
+
{this.props.t('AccountView.AccountTab')}
+ {accessTokensUIEnabled &&
{this.props.t('AccountView.AccessTokensTab')}
}
@@ -107,13 +110,14 @@ function asyncValidate(formProps, dispatch, props) {
AccountView.propTypes = {
previousPath: PropTypes.string.isRequired,
- theme: PropTypes.string.isRequired
+ theme: PropTypes.string.isRequired,
+ t: PropTypes.func.isRequired
};
-export default reduxForm({
+export default withTranslation()(reduxForm({
form: 'updateAllSettings',
fields: ['username', 'email', 'currentPassword', 'newPassword'],
validate: validateSettings,
asyncValidate,
asyncBlurFields: ['username', 'email', 'currentPassword']
-}, mapStateToProps, mapDispatchToProps)(AccountView);
+}, mapStateToProps, mapDispatchToProps)(AccountView));
diff --git a/client/modules/User/pages/NewPasswordView.jsx b/client/modules/User/pages/NewPasswordView.jsx
index 1760cc32..d1b27106 100644
--- a/client/modules/User/pages/NewPasswordView.jsx
+++ b/client/modules/User/pages/NewPasswordView.jsx
@@ -4,6 +4,8 @@ import { reduxForm } from 'redux-form';
import classNames from 'classnames';
import { bindActionCreators } from 'redux';
import { Helmet } from 'react-helmet';
+import { withTranslation } from 'react-i18next';
+import i18next from 'i18next';
import NewPasswordForm from '../components/NewPasswordForm';
import * as UserActions from '../actions';
import Nav from '../../../components/Nav';
@@ -20,13 +22,13 @@ function NewPasswordView(props) {
- p5.js Web Editor | New Password
+ {props.t('NewPasswordView.Title')}
-
Set a New Password
+
{props.t('NewPasswordView.Description')}
- The password reset token is invalid or has expired.
+ {props.t('NewPasswordView.TokenInvalidOrExpired')}
@@ -41,21 +43,22 @@ NewPasswordView.propTypes = {
validateResetPasswordToken: PropTypes.func.isRequired,
user: PropTypes.shape({
resetPasswordInvalid: PropTypes.bool
- }).isRequired
+ }).isRequired,
+ t: PropTypes.func.isRequired
};
function validate(formProps) {
const errors = {};
if (!formProps.password) {
- errors.password = 'Please enter a password';
+ errors.password = i18next.t('NewPasswordView.EmptyPassword');
}
if (!formProps.confirmPassword) {
- errors.confirmPassword = 'Please enter a password confirmation';
+ errors.confirmPassword = i18next.t('NewPasswordView.PasswordConfirmation');
}
if (formProps.password !== formProps.confirmPassword) {
- errors.password = 'Passwords must match';
+ errors.password = i18next.t('NewPasswordView.PasswordMismatch');
}
return errors;
@@ -71,8 +74,8 @@ function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
-export default reduxForm({
+export default withTranslation()(reduxForm({
form: 'new-password',
fields: ['password', 'confirmPassword'],
validate
-}, mapStateToProps, mapDispatchToProps)(NewPasswordView);
+}, mapStateToProps, mapDispatchToProps)(NewPasswordView));
diff --git a/client/modules/User/pages/ResetPasswordView.jsx b/client/modules/User/pages/ResetPasswordView.jsx
index 8ff7dac1..46d0b517 100644
--- a/client/modules/User/pages/ResetPasswordView.jsx
+++ b/client/modules/User/pages/ResetPasswordView.jsx
@@ -6,6 +6,7 @@ import classNames from 'classnames';
import { bindActionCreators } from 'redux';
import { reduxForm } from 'redux-form';
import { Helmet } from 'react-helmet';
+import { withTranslation } from 'react-i18next';
import * as UserActions from '../actions';
import ResetPasswordForm from '../components/ResetPasswordForm';
import { validateResetPassword } from '../../../utils/reduxFormUtils';
@@ -23,19 +24,18 @@ function ResetPasswordView(props) {
- p5.js Web Editor | Reset Password
+ {props.t('ResetPasswordView.Title')}
-
Reset Your Password
+
{props.t('ResetPasswordView.Reset')}
- Your password reset email should arrive shortly. If you don't see it, check
- in your spam folder as sometimes it can end up there.
+ {props.t('ResetPasswordView.Submitted')}
- Log In
- or
- Sign Up
+ {props.t('ResetPasswordView.Login')}
+ {props.t('ResetPasswordView.LoginOr')}
+ {props.t('ResetPasswordView.SignUp')}
@@ -48,6 +48,7 @@ ResetPasswordView.propTypes = {
user: PropTypes.shape({
resetPasswordInitiate: PropTypes.bool
}).isRequired,
+ t: PropTypes.func.isRequired
};
function mapStateToProps(state) {
@@ -60,8 +61,8 @@ function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
-export default reduxForm({
+export default withTranslation()(reduxForm({
form: 'reset-password',
fields: ['email'],
validate: validateResetPassword
-}, mapStateToProps, mapDispatchToProps)(ResetPasswordView);
+}, mapStateToProps, mapDispatchToProps)(ResetPasswordView));
diff --git a/client/utils/reduxFormUtils.js b/client/utils/reduxFormUtils.js
index 8ff4b0a9..27d23a9d 100644
--- a/client/utils/reduxFormUtils.js
+++ b/client/utils/reduxFormUtils.js
@@ -1,4 +1,5 @@
/* eslint-disable */
+import i18n from 'i18next';
export const domOnlyProps = ({
initialValue,
autofill,
@@ -20,19 +21,19 @@ const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))
function validateNameEmail(formProps, errors) {
if (!formProps.username) {
- errors.username = 'Please enter a username.';
+ errors.username = i18n.t('ReduxFormUtils.errorEmptyUsername');
} else if (!formProps.username.match(/^.{1,20}$/)) {
- errors.username = 'Username must be less than 20 characters.';
+ errors.username = i18n.t('ReduxFormUtils.errorLongUsername');
} else if (!formProps.username.match(/^[a-zA-Z0-9._-]{1,20}$/)) {
- errors.username = 'Username must only consist of numbers, letters, periods, dashes, and underscores.';
+ errors.username = i18n.t('ReduxFormUtils.errorValidUsername');
}
if (!formProps.email) {
- errors.email = 'Please enter an email.';
+ errors.email = i18n.t('ReduxFormUtils.errorEmptyEmail');
} else if (
// eslint-disable-next-line max-len
!formProps.email.match(EMAIL_REGEX)) {
- errors.email = 'Please enter a valid email address.';
+ errors.email = i18n.t('ReduxFormUtils.errorInvalidEmail');
}
}
@@ -42,10 +43,10 @@ export function validateSettings(formProps) {
validateNameEmail(formProps, errors);
if (formProps.currentPassword && !formProps.newPassword) {
- errors.newPassword = 'Please enter a new password or leave the current password empty.';
+ errors.newPassword = i18n.t('ReduxFormUtils.errorNewPassword');
}
if (formProps.newPassword && formProps.newPassword.length < 6) {
- errors.newPassword = 'Password must be at least 6 characters';
+ errors.newPassword = i18n.t('ReduxFormUtils.errorShortPassword');
}
return errors;
}
@@ -53,10 +54,10 @@ export function validateSettings(formProps) {
export function validateLogin(formProps) {
const errors = {};
if (!formProps.email) {
- errors.email = 'Please enter an email';
+ errors.email = i18n.t('ReduxFormUtils.errorEmptyEmail');
}
if (!formProps.password) {
- errors.password = 'Please enter a password';
+ errors.password = i18n.t('ReduxFormUtils.errorEmptyPassword');
}
return errors;
}
@@ -67,17 +68,17 @@ export function validateSignup(formProps) {
validateNameEmail(formProps, errors);
if (!formProps.password) {
- errors.password = 'Please enter a password';
+ errors.password = i18n.t('ReduxFormUtils.errorEmptyPassword');
}
if (formProps.password && formProps.password.length < 6) {
- errors.password = 'Password must be at least 6 characters';
+ errors.password = i18n.t('ReduxFormUtils.errorShortPassword');
}
if (!formProps.confirmPassword) {
- errors.confirmPassword = 'Please enter a password confirmation';
+ errors.confirmPassword = i18n.t('ReduxFormUtils.errorConfirmPassword');
}
if (formProps.password !== formProps.confirmPassword && formProps.confirmPassword) {
- errors.confirmPassword = 'Passwords must match';
+ errors.confirmPassword = i18n.t('ReduxFormUtils.errorPasswordMismatch');
}
return errors;
@@ -85,11 +86,11 @@ export function validateSignup(formProps) {
export function validateResetPassword(formProps) {
const errors = {};
if (!formProps.email) {
- errors.email = 'Please enter an email.';
+ errors.email = i18n.t('ReduxFormUtils.errorEmptyEmail');
} else if (
// eslint-disable-next-line max-len
!formProps.email.match(EMAIL_REGEX)) {
- errors.email = 'Please enter a valid email address.';
+ errors.email = i18n.t('ReduxFormUtils.errorInvalidEmail');
}
return errors;
}
diff --git a/translations/locales/en-US/translations.json b/translations/locales/en-US/translations.json
index 652eece4..c12d097c 100644
--- a/translations/locales/en-US/translations.json
+++ b/translations/locales/en-US/translations.json
@@ -183,21 +183,20 @@
"Error": "Error",
"Save": "Save",
"p5logoARIA": "p5.js Logo"
-
},
"IDEView": {
"SubmitFeedback": "Submit Feedback"
},
"NewFileModal": {
"Title": "Create File",
- "CloseButtonARIA": "Close New File Modal",
+ "CloseButtonARIA": "Close New File Modal",
"EnterName": "Please enter a name",
"InvalidType": "Invalid file type. Valid extensions are .js, .css, .json, .txt, .csv, .tsv, .frag, and .vert."
},
"NewFileForm": {
- "AddFileSubmit": "Add File",
+ "AddFileSubmit": "Add File",
"Placeholder": "Name"
-},
+ },
"NewFolderModal": {
"Title": "Create Folder",
"CloseButtonARIA": "Close New Folder Modal",
@@ -208,5 +207,87 @@
"NewFolderForm": {
"AddFolderSubmit": "Add Folder",
"Placeholder": "Name"
+ },
+ "ResetPasswordForm": {
+ "Email": "Email used for registration",
+ "EmailARIA": "email",
+ "Submit": "Send Password Reset Email"
+ },
+ "ResetPasswordView": {
+ "Title": "p5.js Web Editor | Reset Password",
+ "Reset": "Reset Your Password",
+ "Submitted": "Your password reset email should arrive shortly. If you don't see it, check\n in your spam folder as sometimes it can end up there.",
+ "Login": "Log In",
+ "LoginOr": "or",
+ "SignUp": "Sign Up"
+ },
+ "ReduxFormUtils": {
+ "errorInvalidEmail": "Please enter a valid email address",
+ "errorEmptyEmail": "Please enter an email",
+ "errorPasswordMismatch": "Passwords must match",
+ "errorEmptyPassword": "Please enter a password",
+ "errorShortPassword": "Password must be at least 6 characters",
+ "errorConfirmPassword": "Please enter a password confirmation",
+ "errorNewPassword": "Please enter a new password or leave the current password empty.",
+ "errorEmptyUsername": "Please enter a username.",
+ "errorLongUsername": "Username must be less than 20 characters.",
+ "errorValidUsername": "Username must only consist of numbers, letters, periods, dashes, and underscores."
+ },
+ "NewPasswordView": {
+ "Title": "p5.js Web Editor | New Password",
+ "Description": "Set a New Password",
+ "TokenInvalidOrExpired": "The password reset token is invalid or has expired.",
+ "EmptyPassword": "Please enter a password",
+ "PasswordConfirmation": "Please enter a password confirmation",
+ "PasswordMismatch": "Passwords must match"
+ },
+ "AccountForm": {
+ "Email": "Email",
+ "EmailARIA": "email",
+ "Unconfirmed": "Unconfirmed.",
+ "EmailSent": "Confirmation sent, check your email.",
+ "Resend": "Resend confirmation email",
+ "UserName": "User Name",
+ "UserNameARIA": "Username",
+ "CurrentPassword": "Current Password",
+ "CurrentPasswordARIA": "Current Password",
+ "NewPassword": "New Password",
+ "NewPasswordARIA": "New Password",
+ "SubmitSaveAllSettings": "Save All Settings"
+ },
+ "AccountView": {
+ "SocialLogin": "Social Login",
+ "SocialLoginDescription": "Use your GitHub or Google account to log into the p5.js Web Editor.",
+ "Title": "p5.js Web Editor | Account Settings",
+ "Settings": "Account Settings",
+ "AccountTab": "Account",
+ "AccessTokensTab": "Access Tokens"
+ },
+ "APIKeyForm": {
+ "ConfirmDelete": "Are you sure you want to delete {{key_label}}?",
+ "Summary": "Personal Access Tokens act like your password to allow automated\n scripts to access the Editor API. Create a token for each script\n that needs access.",
+ "CreateToken": "Create new token",
+ "TokenLabel": "What is this token for?",
+ "TokenPlaceholder": "What is this token for? e.g. Example import script",
+ "CreateTokenSubmit": "Create",
+ "NoTokens": "You have no existing tokens.",
+ "NewTokenTitle": "Your new access token",
+ "NewTokenInfo": "Make sure to copy your new personal access token now.\n You won’t be able to see it again!",
+ "ExistingTokensTitle": "Existing tokens"
+ },
+ "APIKeyList": {
+ "Name": "Name",
+ "Created": "Created on",
+ "LastUsed": "Last used",
+ "Actions": "Actions",
+ "Never": "Never",
+ "DeleteARIA": "Delete API Key"
+ },
+ "NewPasswordForm": {
+ "Title": "Password",
+ "TitleARIA": "Password",
+ "ConfirmPassword": "Confirm Password",
+ "ConfirmPasswordARIA": "Confirm Password",
+ "SubmitSetNewPassword": "Set New Password"
}
}
diff --git a/translations/locales/es-419/translations.json b/translations/locales/es-419/translations.json
index 5f3efaf7..0104b718 100644
--- a/translations/locales/es-419/translations.json
+++ b/translations/locales/es-419/translations.json
@@ -188,8 +188,8 @@
"SubmitFeedback": "Enviar retroalimentación"
},
"NewFileModal": {
- "Title": "Crear Archivo",
- "CloseButtonARIA": "Cerrar diálogo de crear archivo",
+ "Title": "Crear Archivo",
+ "CloseButtonARIA": "Cerrar diálogo de crear archivo",
"EnterName": "Por favor introduce un nombre",
"InvalidType": "Tipo de archivo inválido. Las extensiones válidas son .js, .css, .json, .txt, .csv, .tsv, .frag y .vert."
},
@@ -198,18 +198,96 @@
"Placeholder": "Nombre"
},
"NewFolderModal": {
- "Title": "Crear Directorio",
- "CloseButtonARIA": "Cerrar Diálogo de Nuevo Directorio",
- "EnterName": "Por favor introduce un nombre",
- "EmptyName": " El nombre del directorio no debe contener solo espacios vacíos",
- "InvalidExtension": "El nombre del directorio no debe contener una extensión"
-},
+ "Title": "Crear Directorio",
+ "CloseButtonARIA": "Cerrar Diálogo de Nuevo Directorio",
+ "EnterName": "Por favor introduce un nombre",
+ "EmptyName": " El nombre del directorio no debe contener solo espacios vacíos",
+ "InvalidExtension": "El nombre del directorio no debe contener una extensión"
+ },
"NewFolderForm": {
"AddFolderSubmit": "Agregar Directorio",
"Placeholder": "Nombre"
+ },
+ "ResetPasswordForm": {
+ "Email": "Correo electrónico usado al registrarse",
+ "EmailARIA": "correo electrónico",
+ "Submit": "Enviar correo para regenerar contraseña"
+ },
+ "ResetPasswordView": {
+ "Title": "Editor Web p5.js | Regenerar Contraseña",
+ "Reset": "Regenerar Contraseña",
+ "Submitted": "Your password reset email should arrive shortly. If you don't see it, check\n in your spam folder as sometimes it can end up there.",
+ "Login": "Ingresa",
+ "LoginOr": "o",
+ "SignUp": "Registráte"
+ },
+ "ReduxFormUtils": {
+ "errorInvalidEmail": "Por favor introduce un correo electrónico válido",
+ "errorEmptyEmail": "Por favor introduce un correo electrónico",
+ "errorPasswordMismatch": "Las contraseñas deben coincidir",
+ "errorEmptyPassword": "Por favor introduce una contraseña",
+ "errorShortPassword": "La contraseña debe tener al menos 6 caracteres",
+ "errorConfirmPassword": "Por favor confirma una contraseña",
+ "errorNewPassword": "Por favor introduce una nueva contraseña o deja la actual contraseña vacía",
+ "errorEmptyUsername": "Por favor introduce tu identificación",
+ "errorLongUsername": "La identificación debe ser menor a 20 caracteres.",
+ "errorValidUsername": "La identificación debe consistir solamente de números, letras, puntos, guiones y guiones bajos."
+ },
+ "NewPasswordView": {
+ "Title": "Editor Web p5.js | Nueva Contraseña",
+ "Description": "Define una nueva contraseña",
+ "TokenInvalidOrExpired": "El token para regenerar la contraseña es inválido o ha expirado.",
+ "EmptyPassword": "Por favor introduce una contraseña",
+ "PasswordConfirmation": "Por favor confirma la contraseña",
+ "PasswordMismatch": "Las contraseña deben coincidir"
+ },
+ "AccountForm": {
+ "Email": "Correo Electrónico",
+ "EmailARIA": "correo electrónico",
+ "Unconfirmed": "Sin confirmar.",
+ "EmailSent": "Confirmación enviada, revisa tu correo electrónico.",
+ "Resend": "Reenviar correo de confirmación",
+ "UserName": "Nombre de Identificación",
+ "UserNameARIA": "Nombre de identificación",
+ "CurrentPassword": "Contraseña Actual",
+ "CurrentPasswordARIA": "Contraseña Actual",
+ "NewPassword": "Nueva Contraseña",
+ "NewPasswordARIA": "Nueva Contraseña",
+ "SubmitSaveAllSettings": "Guardar Todas Las Configuraciones"
+ },
+ "AccountView": {
+ "SocialLogin": "Identificacion usando redes sociales",
+ "SocialLoginDescription": "Usa tu cuenta de GitHub o Google para acceder al Editor Web de p5.js .",
+ "Title": "Editor Web p5.js | Configuración Cuenta",
+ "Settings": "Configuración de la Cuenta",
+ "AccountTab": "Cuenta",
+ "AccessTokensTab": "Tokens de acceso"
+ },
+ "APIKeyForm": {
+ "ConfirmDelete": "¿Estas seguro que quieres borrar {{key_label}}?",
+ "Summary": " Los Tokens de acceso personal actuan como tu contraseña para permitir\n a scripts automáticos acceder al API del Editor. Crea un token por cada script \n que necesite acceso.",
+ "CreateToken": "Crear nuevo token",
+ "TokenLabel": "¿Para que es este token?",
+ "TokenPlaceholder": "¿Para que es este token? p.e. Ejemplo para Importar un Archivo",
+ "CreateTokenSubmit": "Crear",
+ "NoTokens": "No tienes tokens.",
+ "NewTokenTitle": "Tu nuevo token de acceso",
+ "NewTokenInfo": "Asegurate de copiar tu token ahora mismo.\n ¡No podras verlo de nuevo!",
+ "ExistingTokensTitle": "Tokens existentes"
+ },
+ "APIKeyList": {
+ "Name": "Nombre",
+ "Created": "Creado en",
+ "LastUsed": "Usado por última vez",
+ "Actions": "Acciones",
+ "Never": "Nunca",
+ "DeleteARIA": "Borrar clave de API"
+ },
+ "NewPasswordForm": {
+ "Title": "Contraseña",
+ "TitleARIA": "Contraseña",
+ "ConfirmPassword": "Confirmar Contraseña",
+ "ConfirmPasswordARIA": "Confirmar contraseña",
+ "SubmitSetNewPassword": "Crear Nueva Contraseña"
}
}
-
-
-
-