diff --git a/chili-and-cilantro-api/src/controllers/api/game.ts b/chili-and-cilantro-api/src/controllers/api/game.ts index 54464db..63e8415 100644 --- a/chili-and-cilantro-api/src/controllers/api/game.ts +++ b/chili-and-cilantro-api/src/controllers/api/game.ts @@ -87,7 +87,7 @@ export class GameController extends BaseController { .notEmpty() .matches( constants.USER_DISPLAY_NAME_REGEX, - constants.USER_DISPLAY_NAME_REGEX_ERROR, + translate(StringNames.Validation_DisplayNameRegexErrorTemplate), ), body('password') .optional() @@ -128,7 +128,7 @@ export class GameController extends BaseController { .notEmpty() .matches( constants.USER_DISPLAY_NAME_REGEX, - constants.USER_DISPLAY_NAME_REGEX_ERROR, + translate(StringNames.Validation_DisplayNameRegexErrorTemplate), ), ], }), diff --git a/chili-and-cilantro-api/src/controllers/api/user.ts b/chili-and-cilantro-api/src/controllers/api/user.ts index 75f478c..34f929c 100644 --- a/chili-and-cilantro-api/src/controllers/api/user.ts +++ b/chili-and-cilantro-api/src/controllers/api/user.ts @@ -76,7 +76,9 @@ export class UserController extends BaseController { ), body('displayname') .matches(constants.USER_DISPLAY_NAME_REGEX) - .withMessage(constants.USER_DISPLAY_NAME_REGEX_ERROR), + .withMessage( + translate(StringNames.Validation_DisplayNameRegexErrorTemplate), + ), body('email') .isEmail() .withMessage(translate(StringNames.Validation_InvalidEmail)), diff --git a/chili-and-cilantro-api/src/controllers/base.ts b/chili-and-cilantro-api/src/controllers/base.ts index fbd9b92..2420cbb 100644 --- a/chili-and-cilantro-api/src/controllers/base.ts +++ b/chili-and-cilantro-api/src/controllers/base.ts @@ -383,14 +383,12 @@ export abstract class BaseController { res, next, ); - Promise.reject(); - return; + return Promise.reject(); } const user = await UserModel.findById(req.user.id); if (!user) { handleError(new UserNotFoundError(), res, next); - Promise.reject(); - return; + return Promise.reject(); } return user; } diff --git a/chili-and-cilantro-api/src/middlewares/authenticate-token.ts b/chili-and-cilantro-api/src/middlewares/authenticate-token.ts index aee3f54..8570171 100644 --- a/chili-and-cilantro-api/src/middlewares/authenticate-token.ts +++ b/chili-and-cilantro-api/src/middlewares/authenticate-token.ts @@ -28,7 +28,7 @@ export function findAuthToken(headers: IncomingHttpHeaders): string | null { /** * Middleware to authenticate a token - * @param getModel Function to get a model + * @param application The application * @param req The request * @param res The response * @param next The next function diff --git a/chili-and-cilantro-api/src/services/game.ts b/chili-and-cilantro-api/src/services/game.ts index 33f4fd2..7033bda 100644 --- a/chili-and-cilantro-api/src/services/game.ts +++ b/chili-and-cilantro-api/src/services/game.ts @@ -18,17 +18,16 @@ import { InvalidActionError, InvalidGameError, InvalidGameNameError, - InvalidGameParameterError, InvalidGamePasswordError, InvalidMessageError, InvalidUserDisplayNameError, ModelName, + MustBeMasterChefError, NotEnoughChefsError, - NotMasterChefError, + NotYourTurnError, OutOfIngredientError, - OutOfOrderError, StringNames, - TooManyChefsInGameError, + TooManyChefsError, TurnAction, UsernameInUseError, constants, @@ -161,10 +160,11 @@ export class GameService extends BaseService { ) { throw new InvalidGamePasswordError(); } - if (maxChefs < constants.MIN_CHEFS || maxChefs > constants.MAX_CHEFS) { - throw new InvalidGameParameterError( - `Must be between ${constants.MIN_CHEFS} and ${constants.MAX_CHEFS}.`, - ); + if (maxChefs < constants.MIN_CHEFS) { + throw new NotEnoughChefsError(maxChefs); + } + if (maxChefs > constants.MAX_CHEFS) { + throw new TooManyChefsError(); } } @@ -175,6 +175,8 @@ export class GameService extends BaseService { * @param gameName The name of the game * @param password Optional password for the game. Empty string for no password. * @param maxChefs The maximum number of chefs in the game. Must be between MIN_CHEFS and MAX_CHEFS. + * @param gameId The id of the game + * @param masterChefId The id of the master chef * @returns */ public async createGameAsync( @@ -184,7 +186,7 @@ export class GameService extends BaseService { password: string, maxChefs: number, gameId: DefaultIdType = new Types.ObjectId(), - chefId: DefaultIdType = new Types.ObjectId(), + masterChefId: DefaultIdType = new Types.ObjectId(), ): Promise<{ game: IGameDocument; chef: IChefDocument; @@ -196,13 +198,13 @@ export class GameService extends BaseService { { _id: gameId, cardsPlaced: 0, - chefIds: [chefId], + chefIds: [masterChefId], code: gameCode, currentBid: constants.NONE, currentChef: constants.NONE, currentPhase: GamePhase.LOBBY, currentRound: constants.NONE, - masterChefId: chefId, + masterChefId: masterChefId, masterChefUserId: user._id, maxChefs: maxChefs, name: gameName, @@ -221,7 +223,7 @@ export class GameService extends BaseService { user, displayName, true, - chefId, + masterChefId, ); const action = await this.actionService.createGameAsync(game, chef, user); return { game, chef, action }; @@ -313,7 +315,7 @@ export class GameService extends BaseService { throw new GameInProgressError(); } if (game.chefIds.length > game.maxChefs) { - throw new TooManyChefsInGameError(); + throw new TooManyChefsError(); } if ( !validator.matches( @@ -502,13 +504,13 @@ export class GameService extends BaseService { userId: DefaultIdType, ): Promise { if (!(await this.playerService.isMasterChefAsync(userId, game._id))) { - throw new NotMasterChefError(); + throw new MustBeMasterChefError(); } if (game.currentPhase !== GamePhase.LOBBY) { throw new GameInProgressError(); } if (game.chefIds.length < constants.MIN_CHEFS) { - throw new NotEnoughChefsError(game.chefIds.length, constants.MIN_CHEFS); + throw new NotEnoughChefsError(game.chefIds.length); } } @@ -862,7 +864,7 @@ export class GameService extends BaseService { } const currentChefId = this.getGameCurrentChefId(game); if (currentChefId.toString() !== chef._id.toString()) { - throw new OutOfOrderError(); + throw new NotYourTurnError(); } if ( chef.placedCards.length >= constants.HAND_SIZE || @@ -1140,7 +1142,7 @@ export class GameService extends BaseService { return this.withTransaction(async () => { const game = await this.getGameByCodeOrThrowAsync(gameCode, true); if (game.chefIds[game.currentChef].toString() !== user._id.toString()) { - throw new OutOfOrderError(); + throw new NotYourTurnError(); } const chef = await this.chefService.getGameChefOrThrowAsync(game, user); // BID, INCREASE_BID, PASS, or PLACE_CARD diff --git a/chili-and-cilantro-api/src/services/jwt.ts b/chili-and-cilantro-api/src/services/jwt.ts index 8b142de..ad51cec 100644 --- a/chili-and-cilantro-api/src/services/jwt.ts +++ b/chili-and-cilantro-api/src/services/jwt.ts @@ -33,7 +33,7 @@ export class JwtService extends BaseService { const token = sign(tokenUser, environment.jwtSecret, { algorithm: constants.JWT_ALGO, allowInsecureKeySizes: false, - expiresIn: constants.JWT_EXPIRATION, + expiresIn: constants.JWT_EXPIRATION_SEC, }); return { token, @@ -60,11 +60,10 @@ export class JwtService extends BaseService { return { userId: decoded.userId as string, }; - } else { - throw new InvalidTokenError(); } } catch (error) { - throw new InvalidTokenError(); + // Do nothing } + throw new InvalidTokenError(); } } diff --git a/chili-and-cilantro-api/test/unit/gameService.createGame.test.ts b/chili-and-cilantro-api/test/unit/gameService.createGame.test.ts index 43632fa..664ab52 100644 --- a/chili-and-cilantro-api/test/unit/gameService.createGame.test.ts +++ b/chili-and-cilantro-api/test/unit/gameService.createGame.test.ts @@ -1,15 +1,16 @@ import { - AlreadyJoinedOtherError, + ChefAlreadyJoinedError, constants, IChefDocument, ICreateGameActionDocument, IGameDocument, InvalidGameNameError, - InvalidGameParameterError, InvalidGamePasswordError, InvalidUserDisplayNameError, IUserDocument, ModelName, + NotEnoughChefsError, + TooManyChefsError, } from '@chili-and-cilantro/chili-and-cilantro-lib'; import { IApplication } from '@chili-and-cilantro/chili-and-cilantro-node-lib'; import { Model } from 'mongoose'; @@ -108,7 +109,7 @@ describe('GameService', () => { password, maxChefs, ), - ).rejects.toThrow(AlreadyJoinedOtherError); + ).rejects.toThrow(ChefAlreadyJoinedError); }); it('should throw an error for an invalid username with special characters', async () => { @@ -299,7 +300,7 @@ describe('GameService', () => { password, maxChefs, ), - ).rejects.toThrow(InvalidGameParameterError); + ).rejects.toThrow(NotEnoughChefsError); }); it('should throw an error to too many chefs', async () => { @@ -320,7 +321,7 @@ describe('GameService', () => { password, maxChefs, ), - ).rejects.toThrow(InvalidGameParameterError); + ).rejects.toThrow(TooManyChefsError); }); }); diff --git a/chili-and-cilantro-api/test/unit/gameService.joinGameAsync.test.ts b/chili-and-cilantro-api/test/unit/gameService.joinGameAsync.test.ts index e9fc4ac..bc7c4de 100644 --- a/chili-and-cilantro-api/test/unit/gameService.joinGameAsync.test.ts +++ b/chili-and-cilantro-api/test/unit/gameService.joinGameAsync.test.ts @@ -1,8 +1,7 @@ import { - AlreadyJoinedOtherError, + ChefAlreadyJoinedError, constants, DefaultIdType, - GameFullError, GameInProgressError, GamePasswordMismatchError, GamePhase, @@ -11,6 +10,7 @@ import { InvalidUserDisplayNameError, IUserDocument, ModelName, + TooManyChefsError, UsernameInUseError, } from '@chili-and-cilantro/chili-and-cilantro-lib'; import { IApplication } from '@chili-and-cilantro/chili-and-cilantro-node-lib'; @@ -112,7 +112,7 @@ describe('GameService', () => { userDisplayName, game.password, ), - ).rejects.toThrow(AlreadyJoinedOtherError); + ).rejects.toThrow(ChefAlreadyJoinedError); }); it('should throw an error when the chef name is already in the specified game', async () => { // arrange @@ -256,7 +256,7 @@ describe('GameService', () => { userDisplayName, game.password, ), - ).rejects.toThrow(GameFullError); + ).rejects.toThrow(TooManyChefsError); }); it('should allow a user to join a game with no password as long as none is provided', async () => { const game = generateGame(false); // Game with no password required diff --git a/chili-and-cilantro-api/test/unit/gameService.placeIngredient.test.ts b/chili-and-cilantro-api/test/unit/gameService.placeIngredient.test.ts index d99aa75..9ce81b6 100644 --- a/chili-and-cilantro-api/test/unit/gameService.placeIngredient.test.ts +++ b/chili-and-cilantro-api/test/unit/gameService.placeIngredient.test.ts @@ -8,8 +8,8 @@ import { IncorrectGamePhaseError, InvalidActionError, ModelName, + NotYourTurnError, OutOfIngredientError, - OutOfOrderError, TurnAction, constants, } from '@chili-and-cilantro/chili-and-cilantro-lib'; @@ -68,7 +68,7 @@ describe('GameService', () => { game.currentChef = 0; expect(() => gameService.validatePlaceIngredientOrThrow(game, chef, CardType.CHILI), - ).toThrow(OutOfOrderError); + ).toThrow(NotYourTurnError); }); it('should throw an error if the chef has placed all cards or has no cards left', () => { diff --git a/chili-and-cilantro-api/test/unit/gameService.startGame.test.ts b/chili-and-cilantro-api/test/unit/gameService.startGame.test.ts index ce9e32e..1797b2b 100644 --- a/chili-and-cilantro-api/test/unit/gameService.startGame.test.ts +++ b/chili-and-cilantro-api/test/unit/gameService.startGame.test.ts @@ -6,8 +6,8 @@ import { IGameDocument, IUserDocument, ModelName, + MustBeMasterChefError, NotEnoughChefsError, - NotMasterChefError, } from '@chili-and-cilantro/chili-and-cilantro-lib'; import { IApplication } from '@chili-and-cilantro/chili-and-cilantro-node-lib'; import { Model } from 'mongoose'; @@ -159,7 +159,7 @@ describe('gameService startGame', () => { await expect(async () => gameService.validateStartGameOrThrowAsync(game, userId), - ).rejects.toThrow(NotMasterChefError); + ).rejects.toThrow(MustBeMasterChefError); }); it('should throw if the game phase is not LOBBY', async () => { isMasterChefAsync.mockResolvedValue(true); diff --git a/chili-and-cilantro-api/test/unit/userService.test.ts b/chili-and-cilantro-api/test/unit/userService.test.ts index 99f373e..a6d2cad 100644 --- a/chili-and-cilantro-api/test/unit/userService.test.ts +++ b/chili-and-cilantro-api/test/unit/userService.test.ts @@ -1,7 +1,6 @@ import { AccountDeletedError, AccountLockedError, - AccountStatusError, AccountStatusTypeEnum, EmailInUseError, EmailTokenExpiredError, @@ -9,6 +8,7 @@ import { EmailTokenType, EmailTokenUsedOrInvalidError, EmailVerifiedError, + HandleableError, IEmailTokenDocument, IUser, IUserDocument, @@ -179,7 +179,7 @@ describe('UserService', () => { }, { status: 'InvalidStatus' as AccountStatusTypeEnum, - error: AccountStatusError, + error: HandleableError, }, ]; diff --git a/chili-and-cilantro-lib/src/index.ts b/chili-and-cilantro-lib/src/index.ts index 93b5c00..c002541 100644 --- a/chili-and-cilantro-lib/src/index.ts +++ b/chili-and-cilantro-lib/src/index.ts @@ -35,9 +35,9 @@ export * from './lib/errors/email-token-expired'; export * from './lib/errors/email-token-sent-too-recently'; export * from './lib/errors/email-token-used-or-invalid'; export * from './lib/errors/email-verified'; +export * from './lib/errors/game-display-name-in-use'; export * from './lib/errors/game-in-progress'; export * from './lib/errors/game-password-mismatch'; -export * from './lib/errors/game-username-in-use'; export * from './lib/errors/handleable-error'; export * from './lib/errors/incorrect-game-phase'; export * from './lib/errors/invalid-action'; @@ -45,20 +45,19 @@ export * from './lib/errors/invalid-credentials'; export * from './lib/errors/invalid-email'; export * from './lib/errors/invalid-game'; export * from './lib/errors/invalid-game-name'; -export * from './lib/errors/invalid-game-parameter'; export * from './lib/errors/invalid-game-password'; export * from './lib/errors/invalid-message'; export * from './lib/errors/invalid-password'; export * from './lib/errors/invalid-token'; export * from './lib/errors/invalid-user-display-name'; export * from './lib/errors/invalid-username'; +export * from './lib/errors/must-be-master-chef'; export * from './lib/errors/not-enough-chefs'; export * from './lib/errors/not-in-game'; -export * from './lib/errors/not-master-chef'; +export * from './lib/errors/not-your-turn'; export * from './lib/errors/out-of-ingredient'; -export * from './lib/errors/out-of-order'; export * from './lib/errors/pending-email-verification'; -export * from './lib/errors/too-many-chefs-in-game'; +export * from './lib/errors/too-many-chefs'; export * from './lib/errors/user-not-found'; export * from './lib/errors/username-email-required'; export * from './lib/errors/username-in-use'; diff --git a/chili-and-cilantro-lib/src/lib/__snapshots__/i18n.spec.ts.snap b/chili-and-cilantro-lib/src/lib/__snapshots__/i18n.spec.ts.snap index d53b9f7..746160c 100644 --- a/chili-and-cilantro-lib/src/lib/__snapshots__/i18n.spec.ts.snap +++ b/chili-and-cilantro-lib/src/lib/__snapshots__/i18n.spec.ts.snap @@ -56,8 +56,22 @@ exports[`buildNestedI18n should handle English (UK) language 1`] = ` "emailTokenExpired": "Verification link has expired. Please request a new one.", "emailTokenSentTooRecentlyTemplate": "Email token sent too recently. Please try again in {TIME_REMAINING} seconds.", "failedToCreateEmailToken": "Failed to create email token", + "gameAlreadyInProgress": "Game is already in progress.", + "gameDisplayNameAlreadyInUse": "Name is already in use within this game.", + "gameInvalidPhase": "Game is not in the correct phase for this action.", + "gamePasswordMismatch": "Game password does not match.", + "invalidAction": "Invalid action.", + "invalidCredentials": "Invalid credentials.", + "mustBeMasterChef": "You must be the master chef to perform this action.", + "notEnoughChefs": "Not enough chefs to start game. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "You are not in this game.", + "notYourTurn": "Not your turn.", + "outOfIngredient": "Out of {INGREDIENT}.", "sendTokenFailure": "Failed to send email token", "tooManyChefs": "Too many chefs in the kitchen.", + "unexpectedTurnAction": "Unexpected turn action: {TURN_ACTION}.", + "userNotFound": "User not found", + "usernameInUse": "Username is already in use.", "youAlreadyJoined": "You have already joined this game.", }, "forgotPassword": { @@ -112,10 +126,24 @@ exports[`buildNestedI18n should handle English (UK) language 1`] = ` "validation": { "confirmNewPassword": "Confirm new password is required", "currentPasswordRequired": "Current password is required", + "displayNameRegexErrorTemplate": "User display name must be between {MIN_USER_DISPLAY_NAME_LENGTH} and {MAX_USER_DISPLAY_NAME_LENGTH} characters long", + "displayNameRequired": "Display name is required", + "gameCodeRequired": "Game code is required", + "gameNameRequired": "Game name is required", + "gamePasswordRegexErrorTemplate": "Game password must be between {MIN_GAME_PASSWORD_LENGTH} and {MAX_GAME_PASSWORD_LENGTH} characters long", "invalidEmail": "Invalid Email", + "invalidGame": "Invalid game ID or game does not exist.", + "invalidGameCode": "Invalid Game Code", + "invalidGameCodeTemplate": "Game code must be {GAME_CODE_LENGTH} characters long", + "invalidGameName": "Invalid Game Name", + "invalidGameNameTemplate": "Game name must be between {MIN_GAME_NAME_LENGTH} and {MAX_GAME_NAME_LENGTH} characters long.", "invalidLanguage": "Invalid Language", + "invalidMaxChefs": "Invalid number of chefs", + "invalidMessage": "Invalid Message", "invalidTimezone": "Invalid Timezone", "invalidToken": "Invalid Token", + "maxChefsRequired": "Max chefs is required", + "messageRegexErrorTemplate": "Message must be between {MIN_MESSAGE_LENGTH} and {MAX_MESSAGE_LENGTH} characters long.", "newPasswordRequired": "New password is required", "passwordMatch": "Password and confirm must match", "passwordRegexErrorTemplate": "Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters, and contain at least: @@ -190,8 +218,22 @@ exports[`buildNestedI18n should handle English (US) language 1`] = ` "emailTokenExpired": "Verification link has expired. Please request a new one.", "emailTokenSentTooRecentlyTemplate": "Email token sent too recently. Please try again in {TIME_REMAINING} seconds.", "failedToCreateEmailToken": "Failed to create email token", + "gameAlreadyInProgress": "Game is already in progress.", + "gameDisplayNameAlreadyInUse": "Name is already in use within this game.", + "gameInvalidPhase": "Game is not in the correct phase for this action.", + "gamePasswordMismatch": "Game password does not match.", + "invalidAction": "Invalid action.", + "invalidCredentials": "Invalid credentials.", + "mustBeMasterChef": "You must be the master chef to perform this action.", + "notEnoughChefs": "Not enough chefs to start game. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "You are not in this game.", + "notYourTurn": "Not your turn.", + "outOfIngredient": "Out of {INGREDIENT}.", "sendTokenFailure": "Failed to send email token", "tooManyChefs": "Too many chefs in the kitchen", + "unexpectedTurnAction": "Unexpected turn action: {TURN_ACTION}.", + "userNotFound": "User not found", + "usernameInUse": "Username is already in use.", "youAlreadyJoined": "You have already joined this game", }, "forgotPassword": { @@ -246,10 +288,24 @@ exports[`buildNestedI18n should handle English (US) language 1`] = ` "validation": { "confirmNewPassword": "Confirm new password is required", "currentPasswordRequired": "Current password is required", + "displayNameRegexErrorTemplate": "User display name must be between {MIN_USER_DISPLAY_NAME_LENGTH} and {MAX_USER_DISPLAY_NAME_LENGTH} characters long", + "displayNameRequired": "Display name is required", + "gameCodeRequired": "Game code is required", + "gameNameRequired": "Game name is required", + "gamePasswordRegexErrorTemplate": "Game password must be between {MIN_GAME_PASSWORD_LENGTH} and {MAX_GAME_PASSWORD_LENGTH} characters long", "invalidEmail": "Invalid Email", + "invalidGame": "Invalid game ID or game does not exist.", + "invalidGameCode": "Invalid Game Code", + "invalidGameCodeTemplate": "Game code must be {GAME_CODE_LENGTH} characters long", + "invalidGameName": "Invalid Game Name", + "invalidGameNameTemplate": "Game name must be between {MIN_GAME_NAME_LENGTH} and {MAX_GAME_NAME_LENGTH} characters long.", "invalidLanguage": "Invalid Language", + "invalidMaxChefs": "Invalid number of chefs", + "invalidMessage": "Invalid Message", "invalidTimezone": "Invalid Timezone", "invalidToken": "Invalid Token", + "maxChefsRequired": "Max chefs is required", + "messageRegexErrorTemplate": "Message must be between {MIN_MESSAGE_LENGTH} and {MAX_MESSAGE_LENGTH} characters long.", "newPasswordRequired": "New password is required", "passwordMatch": "Password and confirm must match", "passwordRegexErrorTemplate": "Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters, and contain at least: @@ -324,8 +380,22 @@ exports[`buildNestedI18n should handle Español language 1`] = ` "emailTokenExpired": "El enlace de verificación ha caducado. Por favor, solicita uno nuevo.", "emailTokenSentTooRecentlyTemplate": "El token de correo electrónico se envió hace muy poco. Inténtalo de nuevo en {TIME_REMAINING} segundos.", "failedToCreateEmailToken": "Fallo al crear el token de correo", + "gameAlreadyInProgress": "El juego ya está en progreso.", + "gameDisplayNameAlreadyInUse": "El nombre ya está en uso en este juego.", + "gameInvalidPhase": "El juego no está en la fase correcta para esta acción.", + "gamePasswordMismatch": "La contraseña del juego no coincide.", + "invalidAction": "Acción inválida.", + "invalidCredentials": "Credenciales inválidas.", + "mustBeMasterChef": "Debes ser el chef maestro para realizar esta acción.", + "notEnoughChefs": "No hay suficientes chefs para comenzar el juego. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "No estás en este juego.", + "notYourTurn": "No es tu turno.", + "outOfIngredient": "Sin {INGREDIENT}.", "sendTokenFailure": "Fallo al enviar el token de correo", "tooManyChefs": "Demasiados chefs en la cocina", + "unexpectedTurnAction": "Acción de turno inesperada: {TURN_ACTION}.", + "userNotFound": "Usuario no encontrado", + "usernameInUse": "Nombre de usuario en uso", "youAlreadyJoined": "Ya te has unido a este juego", }, "forgotPassword": { @@ -380,10 +450,24 @@ exports[`buildNestedI18n should handle Español language 1`] = ` "validation": { "confirmNewPassword": "Se requiere la confirmación de la nueva contraseña", "currentPasswordRequired": "Se requiere la contraseña actual", + "displayNameRegexErrorTemplate": "El nombre de usuario debe tener entre {MIN_USER_DISPLAY_NAME_LENGTH} y {MAX_USER_DISPLAY_NAME_LENGTH} caracteres", + "displayNameRequired": "Se requiere un nombre de usuario", + "gameCodeRequired": "Se requiere un código de juego", + "gameNameRequired": "Se requiere un nombre de juego", + "gamePasswordRegexErrorTemplate": "La contraseña del juego debe tener entre {MIN_GAME_PASSWORD_LENGTH} y {MAX_GAME_PASSWORD_LENGTH} caracteres", "invalidEmail": "Correo electrónica inválido", + "invalidGame": "ID de juego inválido o juego no existe.", + "invalidGameCode": "Código de juego invático", + "invalidGameCodeTemplate": "El código del juego debe tener {GAME_CODE_LENGTH} caracteres", + "invalidGameName": "Nombre de juego invático", + "invalidGameNameTemplate": "El nombre del juego debe tener entre {MIN_GAME_NAME_LENGTH} y {MAX_GAME_NAME_LENGTH} caracteres.", "invalidLanguage": "Idioma inválido", + "invalidMaxChefs": "Número inválido de chefs", + "invalidMessage": "Mensaje inválido", "invalidTimezone": "Fuseau horaire invático", "invalidToken": "Token inválido", + "maxChefsRequired": "Se requiere un número de chefs", + "messageRegexErrorTemplate": "El mensaje debe tener entre {MIN_MESSAGE_LENGTH} y {MAX_MESSAGE_LENGTH} caracteres.", "newPasswordRequired": "Se requiere la nueva contraseña", "passwordMatch": "La contraseña y la confirmación deben coincidir", "passwordRegexErrorTemplate": "La contraseña debe estar entre {MIN_PASSWORD_LENGTH} y {MAX_PASSWORD_LENGTH} caracteres, y contener al menos: @@ -458,8 +542,22 @@ exports[`buildNestedI18n should handle Français language 1`] = ` "emailTokenExpired": "Le lien de vérification a expiré. Veuillez en demander un nouveau.", "emailTokenSentTooRecentlyTemplate": "Le jeton de courrier électronique a été envoyé trop récemment. Veuillez réessayer dans {TIME_REMAINING} secondes.", "failedToCreateEmailToken": "Échec de la création du jeton par courriel", + "gameAlreadyInProgress": "Le jeu est déjà en cours.", + "gameDisplayNameAlreadyInUse": "Le nom est déjà utilisé dans ce jeu.", + "gameInvalidPhase": "Le jeu n’est pas dans la phase correcte pour cette action.", + "gamePasswordMismatch": "Le mot de passe du jeu ne correspond pas.", + "invalidAction": "Action invalide", + "invalidCredentials": "Identifiants invalides", + "mustBeMasterChef": "Vous devez être le chef de cuisine pour effectuer cette action.", + "notEnoughChefs": "Pas assez de chefs pour commencer le jeu. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "Vous n’êtes pas dans ce jeu.", + "notYourTurn": "Ce n’est pas votre tour.", + "outOfIngredient": "Plus de {INGREDIENT}.", "sendTokenFailure": "Échec de l’envoi du jeton par courriel", "tooManyChefs": "Trop de chefs dans la cuisine", + "unexpectedTurnAction": "Action de tour inattendue: {TURN_ACTION}.", + "userNotFound": "Utilisateur non trouvé", + "usernameInUse": "Nom d’utilisateur déjà utilisé", "youAlreadyJoined": "Vous avez déjà rejoint ce jeu", }, "forgotPassword": { @@ -514,10 +612,24 @@ exports[`buildNestedI18n should handle Français language 1`] = ` "validation": { "confirmNewPassword": "La confirmation du nouveau mot de passe est requise", "currentPasswordRequired": "Le mot de passe actuel est requis", + "displayNameRegexErrorTemplate": "Le nom d’utilisateur doit contenir entre {MIN_USER_DISPLAY_NAME_LENGTH} et {MAX_USER_DISPLAY_NAME_LENGTH} caractères", + "displayNameRequired": "Nom d’utilisateur requis", + "gameCodeRequired": "Code de jeu requis", + "gameNameRequired": "Nom du jeu requis", + "gamePasswordRegexErrorTemplate": "Le mot de passe du jeu doit contenir entre {MIN_GAME_PASSWORD_LENGTH} et {MAX_GAME_PASSWORD_LENGTH} caractères", "invalidEmail": "Courriel invalide", + "invalidGame": "ID de jeu invalide ou jeu inexistant.", + "invalidGameCode": "Code de jeu invalide", + "invalidGameCodeTemplate": "Le code de jeu doit contenir {GAME_CODE_LENGTH} caractères", + "invalidGameName": "Nom de jeu invalide", + "invalidGameNameTemplate": "Le nom du jeu doit contenir entre {MIN_GAME_NAME_LENGTH} et {MAX_GAME_NAME_LENGTH} caractères.", "invalidLanguage": "Langue invalide", + "invalidMaxChefs": "Nombre de chefs invalide", + "invalidMessage": "Message invalide", "invalidTimezone": "Fuseau horaire invalide", "invalidToken": "Jeton invalide", + "maxChefsRequired": "Le nombre de chefs est requis", + "messageRegexErrorTemplate": "Le message doit contenir entre {MIN_MESSAGE_LENGTH} et {MAX_MESSAGE_LENGTH} caractères.", "newPasswordRequired": "Le nouveau mot de passe est requis", "passwordMatch": "Le mot de passe et la confirmation doivent correspondre", "passwordRegexErrorTemplate": "Le mot de passe doit contenir entre {MIN_PASSWORD_LENGTH} et {MAX_PASSWORD_LENGTH} caractères, et contenir au moins : @@ -592,8 +704,22 @@ exports[`buildNestedI18n should handle Український language 1`] = ` "emailTokenExpired": "Термін дії посилання закінчився. Будь ласка, запросіть новий.", "emailTokenSentTooRecentlyTemplate": "Маркер електронної пошти надіслано занадто недавно. Повторіть спробу через {TIME_REMAINING} с.", "failedToCreateEmailToken": "Неможливо створити електронний токен", + "gameAlreadyInProgress": "Гра вже розпочалася", + "gameDisplayNameAlreadyInUse": "Ім’я вже використовується в цій грі.", + "gameInvalidPhase": "Гра не знаходиться в правильній фазі для цієї дії.", + "gamePasswordMismatch": "Пароль гри не відповідає", + "invalidAction": "Недійсна дія", + "invalidCredentials": "Неправильні дані", + "mustBeMasterChef": "Ви повинні бути майстром-кухарем, щоб виконати цю дію.", + "notEnoughChefs": "Недостатньо шеф-кухарів для початку гри. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "Ви не в цій грі", + "notYourTurn": "Це не ваш ход", + "outOfIngredient": "Недостатньо {INGREDIENT}.", "sendTokenFailure": "Неможливо відправити електронний токен", "tooManyChefs": "Забагато шеф-кухарів на кухні", + "unexpectedTurnAction": "Неочікувана дія в ході: {TURN_ACTION}", + "userNotFound": "Користувача не знайдено", + "usernameInUse": "Ім’я користувача вже використовується", "youAlreadyJoined": "Ви вже приєдналися до цієї гри", }, "forgotPassword": { @@ -648,10 +774,24 @@ exports[`buildNestedI18n should handle Український language 1`] = ` "validation": { "confirmNewPassword": "Підтвердження нового пароля є обов’язковим", "currentPasswordRequired": "Поточний пароль є обов’язковим", + "displayNameRegexErrorTemplate": "Ім’я користувача повинно бути від {MIN_USER_DISPLAY_NAME_LENGTH} до {MAX_USER_DISPLAY_NAME_LENGTH} символів", + "displayNameRequired": "Ім’я користувача є обов’язковим", + "gameCodeRequired": "Код гри є обов’язковим", + "gameNameRequired": "Ім’я гри є обов’язковим", + "gamePasswordRegexErrorTemplate": "Пароль гри повинен бути від {MIN_GAME_PASSWORD_LENGTH} до {MAX_GAME_PASSWORD_LENGTH} символів", "invalidEmail": "Недійсний електронній адрес", + "invalidGame": "Недійсний ідентифікатор гри або гра не існує.", + "invalidGameCode": "Недійсний код гри", + "invalidGameCodeTemplate": "Код гри повинен бути {GAME_CODE_LENGTH} символів", + "invalidGameName": "Недійсне ім’я гри", + "invalidGameNameTemplate": "Ім’я гри повинно бути від {MIN_GAME_NAME_LENGTH} до {MAX_GAME_NAME_LENGTH} символів.", "invalidLanguage": "Недійсна мова", + "invalidMaxChefs": "Недійсна кількість шеф-кухарів", + "invalidMessage": "Недійсне повідомлення", "invalidTimezone": "Недійсна часова зона", "invalidToken": "Недійсний токен", + "maxChefsRequired": "Максимальна кількість шеф-кухарів є обов’язковою", + "messageRegexErrorTemplate": "Повідомлення повинно бути від {MIN_MESSAGE_LENGTH} до {MAX_MESSAGE_LENGTH} символів.", "newPasswordRequired": "Новий пароль є обов’язковим", "passwordMatch": "Пароль та підтвердження повинні співпадати", "passwordRegexErrorTemplate": "Пароль повинен бути від {MIN_PASSWORD_LENGTH} до {MAX_PASSWORD_LENGTH} символів, та містити як мінімум: @@ -726,8 +866,22 @@ exports[`buildNestedI18n should handle 中文 language 1`] = ` "emailTokenExpired": "验证链接已过期。请请求一个新的。", "emailTokenSentTooRecentlyTemplate": "电子邮件令牌发送太频繁。请在{TIME_REMAINING}秒后重试。", "failedToCreateEmailToken": "无法创建电子邮件令牌", + "gameAlreadyInProgress": "游戏已经开始。", + "gameDisplayNameAlreadyInUse": "游戏名称已经在使用中", + "gameInvalidPhase": "游戏不处于此操作的正确阶段。", + "gamePasswordMismatch": "游戏密码不匹配", + "invalidAction": "无效操作", + "invalidCredentials": "无效凭证", + "mustBeMasterChef": "您必须是大厨才能执行此操作。", + "notEnoughChefs": "没有足够的厨师开始游戏。{CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "您不在这个游戏中", + "notYourTurn": "不是你的回合", + "outOfIngredient": "缺少{INGREDIENT}", "sendTokenFailure": "无法发送电子邮件令牌", "tooManyChefs": "厨房里有太多厨师", + "unexpectedTurnAction": "意外的回合动作:{TURN_ACTION}", + "userNotFound": "用户未找到", + "usernameInUse": "用户名已经在使用中", "youAlreadyJoined": "您已经加入该游戏", }, "forgotPassword": { @@ -782,10 +936,24 @@ exports[`buildNestedI18n should handle 中文 language 1`] = ` "validation": { "confirmNewPassword": "确认新密码是必需的", "currentPasswordRequired": "当前密码是必需的", + "displayNameRegexErrorTemplate": "用户显示名称必须在{MIN_USER_DISPLAY_NAME_LENGTH}和{MAX_USER_DISPLAY_NAME_LENGTH}个字符之间", + "displayNameRequired": "昵称是必需的", + "gameCodeRequired": "游戏代码是必需的", + "gameNameRequired": "游戏名是必需的", + "gamePasswordRegexErrorTemplate": "游戏密码必须在{MIN_GAME_PASSWORD_LENGTH}和{MAX_GAME_PASSWORD_LENGTH}个字符之间", "invalidEmail": "无效电子邮件", + "invalidGame": "无效游戏ID或游戏不存在。", + "invalidGameCode": "无效游戏代码", + "invalidGameCodeTemplate": "游戏代码必须是{GAME_CODE_LENGTH}个字符长", + "invalidGameName": "无效游戏名称", + "invalidGameNameTemplate": "游戏名称必须在{MIN_GAME_NAME_LENGTH}和{MAX_GAME_NAME_LENGTH}个字符之间。", "invalidLanguage": "无效语言", + "invalidMaxChefs": "无效厨师数量", + "invalidMessage": "无效消息", "invalidTimezone": "无效时区", "invalidToken": "无效令牌", + "maxChefsRequired": "最大厨师是必需的", + "messageRegexErrorTemplate": "消息必须在{MIN_MESSAGE_LENGTH}和{MAX_MESSAGE_LENGTH}个字符之间。", "newPasswordRequired": "新密码是必需的", "passwordMatch": "密码和确认必须匹配", "passwordRegexErrorTemplate": "密码必须在{MIN_PASSWORD_LENGTH}和{MAX_PASSWORD_LENGTH}个字符之间,并且至少包含: @@ -860,8 +1028,22 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "emailTokenExpired": "Verification link has expired. Please request a new one.", "emailTokenSentTooRecentlyTemplate": "Email token sent too recently. Please try again in {TIME_REMAINING} seconds.", "failedToCreateEmailToken": "Failed to create email token", + "gameAlreadyInProgress": "Game is already in progress.", + "gameDisplayNameAlreadyInUse": "Name is already in use within this game.", + "gameInvalidPhase": "Game is not in the correct phase for this action.", + "gamePasswordMismatch": "Game password does not match.", + "invalidAction": "Invalid action.", + "invalidCredentials": "Invalid credentials.", + "mustBeMasterChef": "You must be the master chef to perform this action.", + "notEnoughChefs": "Not enough chefs to start game. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "You are not in this game.", + "notYourTurn": "Not your turn.", + "outOfIngredient": "Out of {INGREDIENT}.", "sendTokenFailure": "Failed to send email token", "tooManyChefs": "Too many chefs in the kitchen", + "unexpectedTurnAction": "Unexpected turn action: {TURN_ACTION}.", + "userNotFound": "User not found", + "usernameInUse": "Username is already in use.", "youAlreadyJoined": "You have already joined this game", }, "forgotPassword": { @@ -916,10 +1098,24 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "validation": { "confirmNewPassword": "Confirm new password is required", "currentPasswordRequired": "Current password is required", + "displayNameRegexErrorTemplate": "User display name must be between {MIN_USER_DISPLAY_NAME_LENGTH} and {MAX_USER_DISPLAY_NAME_LENGTH} characters long", + "displayNameRequired": "Display name is required", + "gameCodeRequired": "Game code is required", + "gameNameRequired": "Game name is required", + "gamePasswordRegexErrorTemplate": "Game password must be between {MIN_GAME_PASSWORD_LENGTH} and {MAX_GAME_PASSWORD_LENGTH} characters long", "invalidEmail": "Invalid Email", + "invalidGame": "Invalid game ID or game does not exist.", + "invalidGameCode": "Invalid Game Code", + "invalidGameCodeTemplate": "Game code must be {GAME_CODE_LENGTH} characters long", + "invalidGameName": "Invalid Game Name", + "invalidGameNameTemplate": "Game name must be between {MIN_GAME_NAME_LENGTH} and {MAX_GAME_NAME_LENGTH} characters long.", "invalidLanguage": "Invalid Language", + "invalidMaxChefs": "Invalid number of chefs", + "invalidMessage": "Invalid Message", "invalidTimezone": "Invalid Timezone", "invalidToken": "Invalid Token", + "maxChefsRequired": "Max chefs is required", + "messageRegexErrorTemplate": "Message must be between {MIN_MESSAGE_LENGTH} and {MAX_MESSAGE_LENGTH} characters long.", "newPasswordRequired": "New password is required", "passwordMatch": "Password and confirm must match", "passwordRegexErrorTemplate": "Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters, and contain at least: @@ -994,8 +1190,22 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "emailTokenExpired": "Verification link has expired. Please request a new one.", "emailTokenSentTooRecentlyTemplate": "Email token sent too recently. Please try again in {TIME_REMAINING} seconds.", "failedToCreateEmailToken": "Failed to create email token", + "gameAlreadyInProgress": "Game is already in progress.", + "gameDisplayNameAlreadyInUse": "Name is already in use within this game.", + "gameInvalidPhase": "Game is not in the correct phase for this action.", + "gamePasswordMismatch": "Game password does not match.", + "invalidAction": "Invalid action.", + "invalidCredentials": "Invalid credentials.", + "mustBeMasterChef": "You must be the master chef to perform this action.", + "notEnoughChefs": "Not enough chefs to start game. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "You are not in this game.", + "notYourTurn": "Not your turn.", + "outOfIngredient": "Out of {INGREDIENT}.", "sendTokenFailure": "Failed to send email token", "tooManyChefs": "Too many chefs in the kitchen.", + "unexpectedTurnAction": "Unexpected turn action: {TURN_ACTION}.", + "userNotFound": "User not found", + "usernameInUse": "Username is already in use.", "youAlreadyJoined": "You have already joined this game.", }, "forgotPassword": { @@ -1050,10 +1260,24 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "validation": { "confirmNewPassword": "Confirm new password is required", "currentPasswordRequired": "Current password is required", + "displayNameRegexErrorTemplate": "User display name must be between {MIN_USER_DISPLAY_NAME_LENGTH} and {MAX_USER_DISPLAY_NAME_LENGTH} characters long", + "displayNameRequired": "Display name is required", + "gameCodeRequired": "Game code is required", + "gameNameRequired": "Game name is required", + "gamePasswordRegexErrorTemplate": "Game password must be between {MIN_GAME_PASSWORD_LENGTH} and {MAX_GAME_PASSWORD_LENGTH} characters long", "invalidEmail": "Invalid Email", + "invalidGame": "Invalid game ID or game does not exist.", + "invalidGameCode": "Invalid Game Code", + "invalidGameCodeTemplate": "Game code must be {GAME_CODE_LENGTH} characters long", + "invalidGameName": "Invalid Game Name", + "invalidGameNameTemplate": "Game name must be between {MIN_GAME_NAME_LENGTH} and {MAX_GAME_NAME_LENGTH} characters long.", "invalidLanguage": "Invalid Language", + "invalidMaxChefs": "Invalid number of chefs", + "invalidMessage": "Invalid Message", "invalidTimezone": "Invalid Timezone", "invalidToken": "Invalid Token", + "maxChefsRequired": "Max chefs is required", + "messageRegexErrorTemplate": "Message must be between {MIN_MESSAGE_LENGTH} and {MAX_MESSAGE_LENGTH} characters long.", "newPasswordRequired": "New password is required", "passwordMatch": "Password and confirm must match", "passwordRegexErrorTemplate": "Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters, and contain at least: @@ -1128,8 +1352,22 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "emailTokenExpired": "Le lien de vérification a expiré. Veuillez en demander un nouveau.", "emailTokenSentTooRecentlyTemplate": "Le jeton de courrier électronique a été envoyé trop récemment. Veuillez réessayer dans {TIME_REMAINING} secondes.", "failedToCreateEmailToken": "Échec de la création du jeton par courriel", + "gameAlreadyInProgress": "Le jeu est déjà en cours.", + "gameDisplayNameAlreadyInUse": "Le nom est déjà utilisé dans ce jeu.", + "gameInvalidPhase": "Le jeu n’est pas dans la phase correcte pour cette action.", + "gamePasswordMismatch": "Le mot de passe du jeu ne correspond pas.", + "invalidAction": "Action invalide", + "invalidCredentials": "Identifiants invalides", + "mustBeMasterChef": "Vous devez être le chef de cuisine pour effectuer cette action.", + "notEnoughChefs": "Pas assez de chefs pour commencer le jeu. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "Vous n’êtes pas dans ce jeu.", + "notYourTurn": "Ce n’est pas votre tour.", + "outOfIngredient": "Plus de {INGREDIENT}.", "sendTokenFailure": "Échec de l’envoi du jeton par courriel", "tooManyChefs": "Trop de chefs dans la cuisine", + "unexpectedTurnAction": "Action de tour inattendue: {TURN_ACTION}.", + "userNotFound": "Utilisateur non trouvé", + "usernameInUse": "Nom d’utilisateur déjà utilisé", "youAlreadyJoined": "Vous avez déjà rejoint ce jeu", }, "forgotPassword": { @@ -1184,10 +1422,24 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "validation": { "confirmNewPassword": "La confirmation du nouveau mot de passe est requise", "currentPasswordRequired": "Le mot de passe actuel est requis", + "displayNameRegexErrorTemplate": "Le nom d’utilisateur doit contenir entre {MIN_USER_DISPLAY_NAME_LENGTH} et {MAX_USER_DISPLAY_NAME_LENGTH} caractères", + "displayNameRequired": "Nom d’utilisateur requis", + "gameCodeRequired": "Code de jeu requis", + "gameNameRequired": "Nom du jeu requis", + "gamePasswordRegexErrorTemplate": "Le mot de passe du jeu doit contenir entre {MIN_GAME_PASSWORD_LENGTH} et {MAX_GAME_PASSWORD_LENGTH} caractères", "invalidEmail": "Courriel invalide", + "invalidGame": "ID de jeu invalide ou jeu inexistant.", + "invalidGameCode": "Code de jeu invalide", + "invalidGameCodeTemplate": "Le code de jeu doit contenir {GAME_CODE_LENGTH} caractères", + "invalidGameName": "Nom de jeu invalide", + "invalidGameNameTemplate": "Le nom du jeu doit contenir entre {MIN_GAME_NAME_LENGTH} et {MAX_GAME_NAME_LENGTH} caractères.", "invalidLanguage": "Langue invalide", + "invalidMaxChefs": "Nombre de chefs invalide", + "invalidMessage": "Message invalide", "invalidTimezone": "Fuseau horaire invalide", "invalidToken": "Jeton invalide", + "maxChefsRequired": "Le nombre de chefs est requis", + "messageRegexErrorTemplate": "Le message doit contenir entre {MIN_MESSAGE_LENGTH} et {MAX_MESSAGE_LENGTH} caractères.", "newPasswordRequired": "Le nouveau mot de passe est requis", "passwordMatch": "Le mot de passe et la confirmation doivent correspondre", "passwordRegexErrorTemplate": "Le mot de passe doit contenir entre {MIN_PASSWORD_LENGTH} et {MAX_PASSWORD_LENGTH} caractères, et contenir au moins : @@ -1262,8 +1514,22 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "emailTokenExpired": "验证链接已过期。请请求一个新的。", "emailTokenSentTooRecentlyTemplate": "电子邮件令牌发送太频繁。请在{TIME_REMAINING}秒后重试。", "failedToCreateEmailToken": "无法创建电子邮件令牌", + "gameAlreadyInProgress": "游戏已经开始。", + "gameDisplayNameAlreadyInUse": "游戏名称已经在使用中", + "gameInvalidPhase": "游戏不处于此操作的正确阶段。", + "gamePasswordMismatch": "游戏密码不匹配", + "invalidAction": "无效操作", + "invalidCredentials": "无效凭证", + "mustBeMasterChef": "您必须是大厨才能执行此操作。", + "notEnoughChefs": "没有足够的厨师开始游戏。{CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "您不在这个游戏中", + "notYourTurn": "不是你的回合", + "outOfIngredient": "缺少{INGREDIENT}", "sendTokenFailure": "无法发送电子邮件令牌", "tooManyChefs": "厨房里有太多厨师", + "unexpectedTurnAction": "意外的回合动作:{TURN_ACTION}", + "userNotFound": "用户未找到", + "usernameInUse": "用户名已经在使用中", "youAlreadyJoined": "您已经加入该游戏", }, "forgotPassword": { @@ -1318,10 +1584,24 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "validation": { "confirmNewPassword": "确认新密码是必需的", "currentPasswordRequired": "当前密码是必需的", + "displayNameRegexErrorTemplate": "用户显示名称必须在{MIN_USER_DISPLAY_NAME_LENGTH}和{MAX_USER_DISPLAY_NAME_LENGTH}个字符之间", + "displayNameRequired": "昵称是必需的", + "gameCodeRequired": "游戏代码是必需的", + "gameNameRequired": "游戏名是必需的", + "gamePasswordRegexErrorTemplate": "游戏密码必须在{MIN_GAME_PASSWORD_LENGTH}和{MAX_GAME_PASSWORD_LENGTH}个字符之间", "invalidEmail": "无效电子邮件", + "invalidGame": "无效游戏ID或游戏不存在。", + "invalidGameCode": "无效游戏代码", + "invalidGameCodeTemplate": "游戏代码必须是{GAME_CODE_LENGTH}个字符长", + "invalidGameName": "无效游戏名称", + "invalidGameNameTemplate": "游戏名称必须在{MIN_GAME_NAME_LENGTH}和{MAX_GAME_NAME_LENGTH}个字符之间。", "invalidLanguage": "无效语言", + "invalidMaxChefs": "无效厨师数量", + "invalidMessage": "无效消息", "invalidTimezone": "无效时区", "invalidToken": "无效令牌", + "maxChefsRequired": "最大厨师是必需的", + "messageRegexErrorTemplate": "消息必须在{MIN_MESSAGE_LENGTH}和{MAX_MESSAGE_LENGTH}个字符之间。", "newPasswordRequired": "新密码是必需的", "passwordMatch": "密码和确认必须匹配", "passwordRegexErrorTemplate": "密码必须在{MIN_PASSWORD_LENGTH}和{MAX_PASSWORD_LENGTH}个字符之间,并且至少包含: @@ -1396,8 +1676,22 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "emailTokenExpired": "El enlace de verificación ha caducado. Por favor, solicita uno nuevo.", "emailTokenSentTooRecentlyTemplate": "El token de correo electrónico se envió hace muy poco. Inténtalo de nuevo en {TIME_REMAINING} segundos.", "failedToCreateEmailToken": "Fallo al crear el token de correo", + "gameAlreadyInProgress": "El juego ya está en progreso.", + "gameDisplayNameAlreadyInUse": "El nombre ya está en uso en este juego.", + "gameInvalidPhase": "El juego no está en la fase correcta para esta acción.", + "gamePasswordMismatch": "La contraseña del juego no coincide.", + "invalidAction": "Acción inválida.", + "invalidCredentials": "Credenciales inválidas.", + "mustBeMasterChef": "Debes ser el chef maestro para realizar esta acción.", + "notEnoughChefs": "No hay suficientes chefs para comenzar el juego. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "No estás en este juego.", + "notYourTurn": "No es tu turno.", + "outOfIngredient": "Sin {INGREDIENT}.", "sendTokenFailure": "Fallo al enviar el token de correo", "tooManyChefs": "Demasiados chefs en la cocina", + "unexpectedTurnAction": "Acción de turno inesperada: {TURN_ACTION}.", + "userNotFound": "Usuario no encontrado", + "usernameInUse": "Nombre de usuario en uso", "youAlreadyJoined": "Ya te has unido a este juego", }, "forgotPassword": { @@ -1452,10 +1746,24 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "validation": { "confirmNewPassword": "Se requiere la confirmación de la nueva contraseña", "currentPasswordRequired": "Se requiere la contraseña actual", + "displayNameRegexErrorTemplate": "El nombre de usuario debe tener entre {MIN_USER_DISPLAY_NAME_LENGTH} y {MAX_USER_DISPLAY_NAME_LENGTH} caracteres", + "displayNameRequired": "Se requiere un nombre de usuario", + "gameCodeRequired": "Se requiere un código de juego", + "gameNameRequired": "Se requiere un nombre de juego", + "gamePasswordRegexErrorTemplate": "La contraseña del juego debe tener entre {MIN_GAME_PASSWORD_LENGTH} y {MAX_GAME_PASSWORD_LENGTH} caracteres", "invalidEmail": "Correo electrónica inválido", + "invalidGame": "ID de juego inválido o juego no existe.", + "invalidGameCode": "Código de juego invático", + "invalidGameCodeTemplate": "El código del juego debe tener {GAME_CODE_LENGTH} caracteres", + "invalidGameName": "Nombre de juego invático", + "invalidGameNameTemplate": "El nombre del juego debe tener entre {MIN_GAME_NAME_LENGTH} y {MAX_GAME_NAME_LENGTH} caracteres.", "invalidLanguage": "Idioma inválido", + "invalidMaxChefs": "Número inválido de chefs", + "invalidMessage": "Mensaje inválido", "invalidTimezone": "Fuseau horaire invático", "invalidToken": "Token inválido", + "maxChefsRequired": "Se requiere un número de chefs", + "messageRegexErrorTemplate": "El mensaje debe tener entre {MIN_MESSAGE_LENGTH} y {MAX_MESSAGE_LENGTH} caracteres.", "newPasswordRequired": "Se requiere la nueva contraseña", "passwordMatch": "La contraseña y la confirmación deben coincidir", "passwordRegexErrorTemplate": "La contraseña debe estar entre {MIN_PASSWORD_LENGTH} y {MAX_PASSWORD_LENGTH} caracteres, y contener al menos: @@ -1530,8 +1838,22 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "emailTokenExpired": "Термін дії посилання закінчився. Будь ласка, запросіть новий.", "emailTokenSentTooRecentlyTemplate": "Маркер електронної пошти надіслано занадто недавно. Повторіть спробу через {TIME_REMAINING} с.", "failedToCreateEmailToken": "Неможливо створити електронний токен", + "gameAlreadyInProgress": "Гра вже розпочалася", + "gameDisplayNameAlreadyInUse": "Ім’я вже використовується в цій грі.", + "gameInvalidPhase": "Гра не знаходиться в правильній фазі для цієї дії.", + "gamePasswordMismatch": "Пароль гри не відповідає", + "invalidAction": "Недійсна дія", + "invalidCredentials": "Неправильні дані", + "mustBeMasterChef": "Ви повинні бути майстром-кухарем, щоб виконати цю дію.", + "notEnoughChefs": "Недостатньо шеф-кухарів для початку гри. {CHEFS_PRESENT}/{MIN_CHEFS}", + "notInGame": "Ви не в цій грі", + "notYourTurn": "Це не ваш ход", + "outOfIngredient": "Недостатньо {INGREDIENT}.", "sendTokenFailure": "Неможливо відправити електронний токен", "tooManyChefs": "Забагато шеф-кухарів на кухні", + "unexpectedTurnAction": "Неочікувана дія в ході: {TURN_ACTION}", + "userNotFound": "Користувача не знайдено", + "usernameInUse": "Ім’я користувача вже використовується", "youAlreadyJoined": "Ви вже приєдналися до цієї гри", }, "forgotPassword": { @@ -1586,10 +1908,24 @@ exports[`buildNestedI18nForLanguage should call buildNestedI18n with the correct "validation": { "confirmNewPassword": "Підтвердження нового пароля є обов’язковим", "currentPasswordRequired": "Поточний пароль є обов’язковим", + "displayNameRegexErrorTemplate": "Ім’я користувача повинно бути від {MIN_USER_DISPLAY_NAME_LENGTH} до {MAX_USER_DISPLAY_NAME_LENGTH} символів", + "displayNameRequired": "Ім’я користувача є обов’язковим", + "gameCodeRequired": "Код гри є обов’язковим", + "gameNameRequired": "Ім’я гри є обов’язковим", + "gamePasswordRegexErrorTemplate": "Пароль гри повинен бути від {MIN_GAME_PASSWORD_LENGTH} до {MAX_GAME_PASSWORD_LENGTH} символів", "invalidEmail": "Недійсний електронній адрес", + "invalidGame": "Недійсний ідентифікатор гри або гра не існує.", + "invalidGameCode": "Недійсний код гри", + "invalidGameCodeTemplate": "Код гри повинен бути {GAME_CODE_LENGTH} символів", + "invalidGameName": "Недійсне ім’я гри", + "invalidGameNameTemplate": "Ім’я гри повинно бути від {MIN_GAME_NAME_LENGTH} до {MAX_GAME_NAME_LENGTH} символів.", "invalidLanguage": "Недійсна мова", + "invalidMaxChefs": "Недійсна кількість шеф-кухарів", + "invalidMessage": "Недійсне повідомлення", "invalidTimezone": "Недійсна часова зона", "invalidToken": "Недійсний токен", + "maxChefsRequired": "Максимальна кількість шеф-кухарів є обов’язковою", + "messageRegexErrorTemplate": "Повідомлення повинно бути від {MIN_MESSAGE_LENGTH} до {MAX_MESSAGE_LENGTH} символів.", "newPasswordRequired": "Новий пароль є обов’язковим", "passwordMatch": "Пароль та підтвердження повинні співпадати", "passwordRegexErrorTemplate": "Пароль повинен бути від {MIN_PASSWORD_LENGTH} до {MAX_PASSWORD_LENGTH} символів, та містити як мінімум: diff --git a/chili-and-cilantro-lib/src/lib/constants.ts b/chili-and-cilantro-lib/src/lib/constants.ts index a55acad..8c7e630 100644 --- a/chili-and-cilantro-lib/src/lib/constants.ts +++ b/chili-and-cilantro-lib/src/lib/constants.ts @@ -8,11 +8,6 @@ export const GAME_CODE_LENGTH = 5; */ export const GAME_CODE_REGEX = new RegExp(`^[A-Z]{${GAME_CODE_LENGTH}}$`); -/** - * Error message for invalid game codes - */ -export const GAME_CODE_REGEX_ERROR = `Game code must be ${GAME_CODE_LENGTH} characters long`; - /** * Maximum number of chefs in a game */ @@ -39,8 +34,6 @@ export const GAME_PASSWORD_REGEX = new RegExp( 'u', ); -export const GAME_PASSWORD_REGEX_ERROR = `Game password must be between ${MIN_GAME_PASSWORD_LENGTH} and ${MAX_GAME_PASSWORD_LENGTH} characters long`; - /** * Maximum length of a game name */ @@ -87,8 +80,6 @@ export const USER_DISPLAY_NAME_REGEX = createUserDisplayNameRegex( MAX_USER_DISPLAY_NAME_LENGTH, ); -export const USER_DISPLAY_NAME_REGEX_ERROR = `User display name must be between ${MIN_USER_DISPLAY_NAME_LENGTH} and ${MAX_USER_DISPLAY_NAME_LENGTH} characters long`; - /** * Maximum age of a game without activity in minutes */ @@ -110,7 +101,12 @@ export const MAX_MESSAGE_LENGTH = 512; /** * Minimum length of a game message */ -export const MIN_MESSAGE_LENGTH = 2; +export const MIN_MESSAGE_LENGTH = 1; + +export const MESSAGE_REGEX = new RegExp( + `^[\\p{L}\\p{M}\\p{Nd}\\p{Pc}\\p{Zs}]{${MIN_MESSAGE_LENGTH},${MAX_MESSAGE_LENGTH}}$`, + 'u', +); /** * Regular expression to look for multilingual strings @@ -160,7 +156,7 @@ export const JWT_ALGO: /** * The expiration time for a JWT token in seconds */ -export const JWT_EXPIRATION = 86400; +export const JWT_EXPIRATION_SEC = 86400; /** * The domain of the site @@ -242,17 +238,16 @@ export default { BCRYPT_ROUNDS, CHILI_PER_HAND, EMAIL_FROM, - EMAIL_TOKEN_RESEND_INTERVAL: EMAIL_TOKEN_RESEND_INTERVAL_MS, + EMAIL_TOKEN_RESEND_INTERVAL_MS, GAME_CODE_LENGTH, GAME_CODE_REGEX, JWT_ALGO, - JWT_EXPIRATION, + JWT_EXPIRATION_SEC, MAX_CHEFS, MIN_CHEFS, MAX_GAME_PASSWORD_LENGTH, MIN_GAME_PASSWORD_LENGTH, GAME_PASSWORD_REGEX, - GAME_PASSWORD_REGEX_ERROR, MAX_GAME_NAME_LENGTH, MIN_GAME_NAME_LENGTH, GAME_NAME_REGEX, @@ -260,12 +255,12 @@ export default { HAND_SIZE, MAX_MESSAGE_LENGTH, MIN_MESSAGE_LENGTH, + MESSAGE_REGEX, MAX_USERNAME_LENGTH, MIN_USERNAME_LENGTH, MAX_USER_DISPLAY_NAME_LENGTH, MIN_USER_DISPLAY_NAME_LENGTH, USER_DISPLAY_NAME_REGEX, - USER_DISPLAY_NAME_REGEX_ERROR, MAX_GAME_AGE_WITHOUT_ACTIVITY_IN_MINUTES, MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH, @@ -274,5 +269,5 @@ export default { ROUNDS_TO_WIN, USERNAME_REGEX, SITE_DOMAIN, - NONE: NONE, + NONE, }; diff --git a/chili-and-cilantro-lib/src/lib/enumerations/string-names.ts b/chili-and-cilantro-lib/src/lib/enumerations/string-names.ts index 3cae85f..9242381 100644 --- a/chili-and-cilantro-lib/src/lib/enumerations/string-names.ts +++ b/chili-and-cilantro-lib/src/lib/enumerations/string-names.ts @@ -44,8 +44,22 @@ export enum StringNames { Error_EmailTokenExpired = 'error_emailTokenExpired', Error_EmailTokenSentTooRecentlyTemplate = 'error_emailTokenSentTooRecentlyTemplate', Error_FailedToCreateEmailToken = 'error_failedToCreateEmailToken', + Error_GameAlreadyInProgress = 'error_gameAlreadyInProgress', + Error_GameDisplayNameAlreadyInUse = 'error_gameDisplayNameAlreadyInUse', + Error_GamePasswordMismatch = 'error_gamePasswordMismatch', + Error_GameInvalidPhase = 'error_gameInvalidPhase', + Error_InvalidAction = 'error_invalidAction', + Error_InvalidCredentials = 'error_invalidCredentials', + Error_MustBeMasterChef = 'error_mustBeMasterChef', + Error_NotEnoughChefsTemplate = 'error_notEnoughChefs', + Error_OutOfIngredientTemplate = 'error_outOfIngredient', + Error_NotInGame = 'error_notInGame', + Error_NotYourTurn = 'error_notYourTurn', Error_SendTokenFailure = 'error_sendTokenFailure', Error_TooManyChefs = 'error_tooManyChefs', + Error_UnexpectedTurnActionTemplate = 'error_unexpectedTurnAction', + Error_UsernameInUse = 'error_usernameInUse', + Error_UserNotFound = 'error_userNotFound', Error_YouAlreadyJoined = 'error_youAlreadyJoined', Dashboard_GamesCreated = 'dashboard_gamesCreated', Dashboard_GamesParticipating = 'dashboard_gamesParticipating', @@ -86,10 +100,24 @@ export enum StringNames { Splash_Description = 'splash_description', Splash_HowToPlay = 'splash_howToPlay', ValidationError = 'validationError', + Validation_GameCodeRequired = 'validation_gameCodeRequired', + Validation_GameNameRequired = 'validation_gameNameRequired', + Validation_GamePasswordRegexErrorTemplate = 'validation_gamePasswordRegexErrorTemplate', + Validation_DisplayNameRegexErrorTemplate = 'validation_displayNameRegexErrorTemplate', + Validation_DisplayNameRequired = 'validation_displayNameRequired', Validation_InvalidEmail = 'validation_invalidEmail', + Validation_InvalidGame = 'validation_invalidGame', + Validation_InvalidGameCode = 'validation_invalidGameCode', + Validation_InvalidGameCodeTemplate = 'validation_invalidGameCodeTemplate', + Validation_InvalidGameName = 'validation_invalidGameName', + Validation_InvalidGameNameTemplate = 'validation_invalidGameNameTemplate', Validation_InvalidLanguage = 'validation_invalidLanguage', + Validation_InvalidMaxChefs = 'validation_invalidMaxChefs', + Validation_InvalidMessage = 'validation_invalidMessage', Validation_InvalidTimezone = 'validation_invalidTimezone', Validation_InvalidToken = 'validation_invalidToken', + Validation_MaxChefsRequired = 'validation_maxChefsRequired', + Validation_MessageRegexErrorTemplate = 'validation_messageRegexErrorTemplate', Validation_PasswordRegexErrorTemplate = 'validation_passwordRegexErrorTemplate', Validation_CurrentPasswordRequired = 'validation_currentPasswordRequired', Validation_PasswordsDifferent = 'validation_passwordsDifferent', diff --git a/chili-and-cilantro-lib/src/lib/errors/email-token-sent-too-recently.ts b/chili-and-cilantro-lib/src/lib/errors/email-token-sent-too-recently.ts index c5625ce..aea7001 100644 --- a/chili-and-cilantro-lib/src/lib/errors/email-token-sent-too-recently.ts +++ b/chili-and-cilantro-lib/src/lib/errors/email-token-sent-too-recently.ts @@ -10,7 +10,7 @@ export class EmailTokenSentTooRecentlyError extends HandleableError { constructor(lastSent: Date) { const now = Date.now(); const nextAvailableTime = new Date( - lastSent.getTime() + constants.EMAIL_TOKEN_RESEND_INTERVAL, + lastSent.getTime() + constants.EMAIL_TOKEN_RESEND_INTERVAL_MS, ); const timeRemaining = Math.max( 0, diff --git a/chili-and-cilantro-lib/src/lib/errors/game-display-name-in-use.ts b/chili-and-cilantro-lib/src/lib/errors/game-display-name-in-use.ts new file mode 100644 index 0000000..e9d8296 --- /dev/null +++ b/chili-and-cilantro-lib/src/lib/errors/game-display-name-in-use.ts @@ -0,0 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; +import { ValidationError } from './validation-error'; + +export class GameDisplayNameInUseError extends ValidationError { + constructor() { + super(translate(StringNames.Error_GameDisplayNameAlreadyInUse)); + this.name = 'GameDisplayNameInUseError'; + Object.setPrototypeOf(this, GameDisplayNameInUseError.prototype); + } +} diff --git a/chili-and-cilantro-lib/src/lib/errors/game-in-progress.ts b/chili-and-cilantro-lib/src/lib/errors/game-in-progress.ts index 4f39083..7e5b9ef 100644 --- a/chili-and-cilantro-lib/src/lib/errors/game-in-progress.ts +++ b/chili-and-cilantro-lib/src/lib/errors/game-in-progress.ts @@ -1,8 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class GameInProgressError extends ValidationError { constructor() { - super('Game is already in progress.'); + super(translate(StringNames.Error_GameAlreadyInProgress)); + this.name = 'GameInProgressError'; Object.setPrototypeOf(this, GameInProgressError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/game-password-mismatch.ts b/chili-and-cilantro-lib/src/lib/errors/game-password-mismatch.ts index 19110f2..98bad29 100644 --- a/chili-and-cilantro-lib/src/lib/errors/game-password-mismatch.ts +++ b/chili-and-cilantro-lib/src/lib/errors/game-password-mismatch.ts @@ -1,8 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class GamePasswordMismatchError extends ValidationError { constructor() { - super('Game password does not match.'); + super(translate(StringNames.Error_GamePasswordMismatch)); + this.name = 'GamePasswordMismatchError'; Object.setPrototypeOf(this, GamePasswordMismatchError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/game-username-in-use.ts b/chili-and-cilantro-lib/src/lib/errors/game-username-in-use.ts deleted file mode 100644 index d2c6a63..0000000 --- a/chili-and-cilantro-lib/src/lib/errors/game-username-in-use.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ValidationError } from './validation-error'; - -export class GameUsernameInUseError extends ValidationError { - constructor() { - super('Name is already in use within this game.'); - Object.setPrototypeOf(this, GameUsernameInUseError.prototype); - } -} diff --git a/chili-and-cilantro-lib/src/lib/errors/incorrect-game-phase.ts b/chili-and-cilantro-lib/src/lib/errors/incorrect-game-phase.ts index 543a937..f9e575a 100644 --- a/chili-and-cilantro-lib/src/lib/errors/incorrect-game-phase.ts +++ b/chili-and-cilantro-lib/src/lib/errors/incorrect-game-phase.ts @@ -1,8 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class IncorrectGamePhaseError extends ValidationError { constructor() { - super('Game is not in the correct phase for this action.'); + super(translate(StringNames.Error_GameInvalidPhase)); + this.name = 'IncorrectGamePhaseError'; Object.setPrototypeOf(this, IncorrectGamePhaseError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-action.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-action.ts index 4738d32..b937468 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-action.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-action.ts @@ -1,20 +1,38 @@ import { CardType } from '../enumerations/card-type'; +import { StringNames } from '../enumerations/string-names'; +import { TranslatableEnumType } from '../enumerations/translatable-enum'; import { TurnAction } from '../enumerations/turn-action'; +import { translate, translateEnum } from '../i18n'; import { ValidationError } from './validation-error'; export class InvalidActionError extends ValidationError { constructor(action: TurnAction, amount?: number, ingredient?: CardType) { - const amountString = amount ? ' ' + amount.toString() : ''; - const ingredientString = ingredient ? ' ' + ingredient.toString() : ''; + const amountString = amount ? amount.toString() : ''; + const ingredientString = ingredient + ? translateEnum({ + type: TranslatableEnumType.CardType, + value: ingredient, + }) + : ''; + const invalidAction = translate(StringNames.Error_InvalidAction); + const actionString = translateEnum({ + type: TranslatableEnumType.TurnAction, + value: action, + }); if (action === TurnAction.Bid || action === TurnAction.IncreaseBid) { - super(`Invalid action: ${action}${amountString}`); + super(`${invalidAction}: ${actionString} - ${amountString}`); } else if (action === TurnAction.PlaceCard) { - super(`Invalid action: ${action}${ingredientString}`); + super(`${invalidAction}: ${actionString} - ${ingredientString}`); } else if (action === TurnAction.Pass) { - super(`Invalid action: ${action}`); + super(`${invalidAction}: ${actionString}`); } else { - throw new Error(`unexpected invalid action: ${action}`); + throw new Error( + translate(StringNames.Error_UnexpectedTurnActionTemplate, undefined, { + TURN_ACTION: `${action}`, + }), + ); } + this.name = 'InvalidActionError'; Object.setPrototypeOf(this, InvalidActionError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-credentials.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-credentials.ts index 02227fa..b8241a8 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-credentials.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-credentials.ts @@ -1,8 +1,10 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { HandleableError } from './handleable-error'; export class InvalidCredentialsError extends HandleableError { constructor() { - super('Invalid credentials', { statusCode: 401 }); + super(translate(StringNames.Error_InvalidCredentials), { statusCode: 401 }); this.name = 'InvalidCredentialsError'; Object.setPrototypeOf(this, InvalidCredentialsError.prototype); } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-game-name.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-game-name.ts index 77e0590..cbf22c3 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-game-name.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-game-name.ts @@ -1,11 +1,11 @@ -import constants from '../constants'; +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class InvalidGameNameError extends ValidationError { constructor() { - super( - `Invalid game name. Must be alphanumeric and between ${constants.MIN_GAME_NAME_LENGTH} and ${constants.MAX_GAME_NAME_LENGTH}.`, - ); + super(translate(StringNames.Validation_InvalidGameNameTemplate)); + this.name = 'InvalidGameNameError'; Object.setPrototypeOf(this, InvalidGameNameError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-game-parameter.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-game-parameter.ts deleted file mode 100644 index 276f307..0000000 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-game-parameter.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ValidationError } from './validation-error'; - -export class InvalidGameParameterError extends ValidationError { - constructor(parameter: string) { - super(`Invalid game parameter: ${parameter}`); - Object.setPrototypeOf(this, InvalidGameParameterError.prototype); - } -} diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-game-password.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-game-password.ts index 169d2ce..79f878e 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-game-password.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-game-password.ts @@ -1,11 +1,11 @@ -import constants from '../constants'; +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class InvalidGamePasswordError extends ValidationError { constructor() { - super( - `Invalid game pssword. Must be alphanumeric and between ${constants.MIN_PASSWORD_LENGTH} and ${constants.MAX_PASSWORD_LENGTH}.`, - ); + super(translate(StringNames.Validation_GamePasswordRegexErrorTemplate)); + this.name = 'InvalidGamePasswordError'; Object.setPrototypeOf(this, InvalidGamePasswordError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-game.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-game.ts index e4cf38d..be05dea 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-game.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-game.ts @@ -1,8 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class InvalidGameError extends ValidationError { constructor() { - super('Invalid game ID or game does not exist.'); + super(translate(StringNames.Validation_InvalidGame)); + this.name = 'InvalidGameError'; Object.setPrototypeOf(this, InvalidGameError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-message.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-message.ts index 1976493..e22f94d 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-message.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-message.ts @@ -1,11 +1,11 @@ -import constants from '../constants'; +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class InvalidMessageError extends ValidationError { constructor() { - super( - `Message must be between ${constants.MIN_MESSAGE_LENGTH} and ${constants.MAX_MESSAGE_LENGTH} characters long.`, - ); + super(translate(StringNames.Validation_MessageRegexErrorTemplate)); + this.name = 'InvalidMessageError'; Object.setPrototypeOf(this, InvalidMessageError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-token.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-token.ts index d2e0abb..4a7ac40 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-token.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-token.ts @@ -1,8 +1,10 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { HandleableError } from './handleable-error'; export class InvalidTokenError extends HandleableError { constructor() { - super('Invalid token', { statusCode: 400 }); + super(translate(StringNames.Validation_InvalidToken), { statusCode: 400 }); this.name = 'InvalidTokenError'; Object.setPrototypeOf(this, InvalidTokenError.prototype); } diff --git a/chili-and-cilantro-lib/src/lib/errors/invalid-user-display-name.ts b/chili-and-cilantro-lib/src/lib/errors/invalid-user-display-name.ts index 5498f00..d146598 100644 --- a/chili-and-cilantro-lib/src/lib/errors/invalid-user-display-name.ts +++ b/chili-and-cilantro-lib/src/lib/errors/invalid-user-display-name.ts @@ -1,11 +1,13 @@ -import constants from '../constants'; +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class InvalidUserDisplayNameError extends ValidationError { + public readonly displayName: string; constructor(displayName: string) { - super( - `Invalid user display name "${displayName}": Must be alphanumeric and between ${constants.MIN_USER_DISPLAY_NAME_LENGTH} and ${constants.MAX_USER_DISPLAY_NAME_LENGTH} characters long.`, - ); + super(translate(StringNames.Validation_DisplayNameRegexErrorTemplate)); + this.displayName = displayName; + this.name = 'InvalidUserDisplayNameError'; Object.setPrototypeOf(this, InvalidUserDisplayNameError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/must-be-master-chef.ts b/chili-and-cilantro-lib/src/lib/errors/must-be-master-chef.ts new file mode 100644 index 0000000..a6dd896 --- /dev/null +++ b/chili-and-cilantro-lib/src/lib/errors/must-be-master-chef.ts @@ -0,0 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; +import { HandleableError } from './handleable-error'; + +export class MustBeMasterChefError extends HandleableError { + constructor() { + super(translate(StringNames.Error_MustBeMasterChef), { statusCode: 422 }); + this.name = 'MustBeMasterChefError'; + Object.setPrototypeOf(this, MustBeMasterChefError.prototype); + } +} diff --git a/chili-and-cilantro-lib/src/lib/errors/not-enough-chefs.ts b/chili-and-cilantro-lib/src/lib/errors/not-enough-chefs.ts index c54ab6b..c48f03e 100644 --- a/chili-and-cilantro-lib/src/lib/errors/not-enough-chefs.ts +++ b/chili-and-cilantro-lib/src/lib/errors/not-enough-chefs.ts @@ -1,10 +1,18 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { HandleableError } from './handleable-error'; export class NotEnoughChefsError extends HandleableError { - constructor(present: number, minChefs: number) { - super(`Not enough chefs to start game. ${present}/${minChefs}`, { - statusCode: 422, - }); + constructor(present: number) { + super( + translate(StringNames.Error_NotEnoughChefsTemplate, undefined, { + CHEFS_PRESENT: `${present}`, + }), + { + statusCode: 422, + }, + ); + this.name = 'NotEnoughChefsError'; Object.setPrototypeOf(this, NotEnoughChefsError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/not-in-game.ts b/chili-and-cilantro-lib/src/lib/errors/not-in-game.ts index adecbcc..cdf63f8 100644 --- a/chili-and-cilantro-lib/src/lib/errors/not-in-game.ts +++ b/chili-and-cilantro-lib/src/lib/errors/not-in-game.ts @@ -1,8 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class NotInGameError extends ValidationError { constructor() { - super('You are not in this game!'); + super(translate(StringNames.Error_NotInGame)); + this.name = 'NotInGameError'; Object.setPrototypeOf(this, NotInGameError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/not-master-chef.ts b/chili-and-cilantro-lib/src/lib/errors/not-master-chef.ts deleted file mode 100644 index 772aa7d..0000000 --- a/chili-and-cilantro-lib/src/lib/errors/not-master-chef.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { HandleableError } from './handleable-error'; - -export class NotMasterChefError extends HandleableError { - constructor() { - super(`User is not the Master Chef`, { statusCode: 422 }); - Object.setPrototypeOf(this, NotMasterChefError.prototype); - } -} diff --git a/chili-and-cilantro-lib/src/lib/errors/not-your-turn.ts b/chili-and-cilantro-lib/src/lib/errors/not-your-turn.ts new file mode 100644 index 0000000..721fabc --- /dev/null +++ b/chili-and-cilantro-lib/src/lib/errors/not-your-turn.ts @@ -0,0 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; +import { ValidationError } from './validation-error'; + +export class NotYourTurnError extends ValidationError { + constructor() { + super(translate(StringNames.Error_NotYourTurn)); + this.name = 'NotYourTurnError'; + Object.setPrototypeOf(this, NotYourTurnError.prototype); + } +} diff --git a/chili-and-cilantro-lib/src/lib/errors/out-of-ingredient.ts b/chili-and-cilantro-lib/src/lib/errors/out-of-ingredient.ts index 0445673..9e99f1f 100644 --- a/chili-and-cilantro-lib/src/lib/errors/out-of-ingredient.ts +++ b/chili-and-cilantro-lib/src/lib/errors/out-of-ingredient.ts @@ -1,9 +1,21 @@ import { CardType } from '../enumerations/card-type'; +import { StringNames } from '../enumerations/string-names'; +import { TranslatableEnumType } from '../enumerations/translatable-enum'; +import { translate, translateEnum } from '../i18n'; import { ValidationError } from './validation-error'; export class OutOfIngredientError extends ValidationError { constructor(ingredient: CardType) { - super(`Not enough of that ingredient. (${ingredient})`); + const ingredientString = translateEnum({ + type: TranslatableEnumType.CardType, + value: ingredient, + }); + super( + translate(StringNames.Error_OutOfIngredientTemplate, undefined, { + INGREDIENT: ingredientString, + }), + ); + this.name = 'OutOfIngredientError'; Object.setPrototypeOf(this, OutOfIngredientError.prototype); } } diff --git a/chili-and-cilantro-lib/src/lib/errors/out-of-order.ts b/chili-and-cilantro-lib/src/lib/errors/out-of-order.ts deleted file mode 100644 index 0242a83..0000000 --- a/chili-and-cilantro-lib/src/lib/errors/out-of-order.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ValidationError } from './validation-error'; - -export class OutOfOrderError extends ValidationError { - constructor() { - super('Not your turn.'); - Object.setPrototypeOf(this, OutOfOrderError.prototype); - } -} diff --git a/chili-and-cilantro-lib/src/lib/errors/too-many-chefs-in-game.ts b/chili-and-cilantro-lib/src/lib/errors/too-many-chefs-in-game.ts deleted file mode 100644 index 09f1d23..0000000 --- a/chili-and-cilantro-lib/src/lib/errors/too-many-chefs-in-game.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ValidationError } from './validation-error'; - -export class TooManyChefsInGameError extends ValidationError { - constructor() { - super('Too many chefs in game'); - this.name = 'TooManyChefsInGameError'; - Object.setPrototypeOf(this, TooManyChefsInGameError.prototype); - } -} diff --git a/chili-and-cilantro-lib/src/lib/errors/too-many-chefs.ts b/chili-and-cilantro-lib/src/lib/errors/too-many-chefs.ts new file mode 100644 index 0000000..a20e980 --- /dev/null +++ b/chili-and-cilantro-lib/src/lib/errors/too-many-chefs.ts @@ -0,0 +1,11 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; +import { ValidationError } from './validation-error'; + +export class TooManyChefsError extends ValidationError { + constructor() { + super(translate(StringNames.Error_TooManyChefs)); + this.name = 'TooManyChefsError'; + Object.setPrototypeOf(this, TooManyChefsError.prototype); + } +} diff --git a/chili-and-cilantro-lib/src/lib/errors/user-not-found.ts b/chili-and-cilantro-lib/src/lib/errors/user-not-found.ts index b5b61b8..91759d5 100644 --- a/chili-and-cilantro-lib/src/lib/errors/user-not-found.ts +++ b/chili-and-cilantro-lib/src/lib/errors/user-not-found.ts @@ -1,8 +1,10 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { HandleableError } from './handleable-error'; export class UserNotFoundError extends HandleableError { constructor() { - super('User not found', { statusCode: 404 }); + super(translate(StringNames.Error_UserNotFound), { statusCode: 404 }); this.name = 'UserNotFound'; Object.setPrototypeOf(this, UserNotFoundError.prototype); } diff --git a/chili-and-cilantro-lib/src/lib/errors/username-email-required.ts b/chili-and-cilantro-lib/src/lib/errors/username-email-required.ts index 175d0ca..5815b93 100644 --- a/chili-and-cilantro-lib/src/lib/errors/username-email-required.ts +++ b/chili-and-cilantro-lib/src/lib/errors/username-email-required.ts @@ -1,8 +1,10 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class UsernameOrEmailRequiredError extends ValidationError { constructor() { - super('Either username or email is required'); + super(translate(StringNames.Login_UsernameOrEmailRequired)); this.name = 'UsernameOrEmailRequiredError'; Object.setPrototypeOf(this, UsernameOrEmailRequiredError.prototype); } diff --git a/chili-and-cilantro-lib/src/lib/errors/username-in-use.ts b/chili-and-cilantro-lib/src/lib/errors/username-in-use.ts index e3d9d54..c9c2b50 100644 --- a/chili-and-cilantro-lib/src/lib/errors/username-in-use.ts +++ b/chili-and-cilantro-lib/src/lib/errors/username-in-use.ts @@ -1,8 +1,10 @@ +import { StringNames } from '../enumerations/string-names'; +import { translate } from '../i18n'; import { ValidationError } from './validation-error'; export class UsernameInUseError extends ValidationError { constructor() { - super('Username is already in use'); + super(translate(StringNames.Error_UsernameInUse)); this.name = 'UsernameInUseError'; Object.setPrototypeOf(this, UsernameInUseError.prototype); } diff --git a/chili-and-cilantro-lib/src/lib/strings/english-uk.ts b/chili-and-cilantro-lib/src/lib/strings/english-uk.ts index 5debe7d..8782c1e 100644 --- a/chili-and-cilantro-lib/src/lib/strings/english-uk.ts +++ b/chili-and-cilantro-lib/src/lib/strings/english-uk.ts @@ -51,8 +51,27 @@ export const BritishEnglishStrings: StringsCollection = { [StringNames.Error_EmailTokenSentTooRecentlyTemplate]: 'Email token sent too recently. Please try again in {TIME_REMAINING} seconds.', [StringNames.Error_FailedToCreateEmailToken]: 'Failed to create email token', + [StringNames.Error_GameAlreadyInProgress]: 'Game is already in progress.', + [StringNames.Error_GameDisplayNameAlreadyInUse]: + 'Name is already in use within this game.', + [StringNames.Error_GameInvalidPhase]: + 'Game is not in the correct phase for this action.', + [StringNames.Error_GamePasswordMismatch]: 'Game password does not match.', + [StringNames.Error_InvalidAction]: 'Invalid action.', + [StringNames.Error_InvalidCredentials]: 'Invalid credentials.', + [StringNames.Error_MustBeMasterChef]: + 'You must be the master chef to perform this action.', + [StringNames.Error_NotEnoughChefsTemplate]: + 'Not enough chefs to start game. {CHEFS_PRESENT}/{MIN_CHEFS}', + [StringNames.Error_NotInGame]: 'You are not in this game.', + [StringNames.Error_NotYourTurn]: 'Not your turn.', + [StringNames.Error_OutOfIngredientTemplate]: 'Out of {INGREDIENT}.', [StringNames.Error_SendTokenFailure]: 'Failed to send email token', [StringNames.Error_TooManyChefs]: 'Too many chefs in the kitchen.', + [StringNames.Error_UnexpectedTurnActionTemplate]: + 'Unexpected turn action: {TURN_ACTION}.', + [StringNames.Error_UsernameInUse]: 'Username is already in use.', + [StringNames.Error_UserNotFound]: 'User not found', [StringNames.Error_YouAlreadyJoined]: 'You have already joined this game.', [StringNames.Dashboard_GamesCreated]: "Games You've Created", [StringNames.Dashboard_GamesParticipating]: "Games You're Participating in", @@ -103,10 +122,30 @@ export const BritishEnglishStrings: StringsCollection = { 'In Chili and Cilantro, aspiring chefs compete to create the perfect dish. Your goal is to add just the right amount of cilantro without ruining it with a scorching chili. Be the first to successfully season two dishes or be the last chef standing to win!', [StringNames.Splash_HowToPlay]: 'How to Play', [StringNames.ValidationError]: 'Validation Error', + [StringNames.Validation_DisplayNameRegexErrorTemplate]: + 'User display name must be between {MIN_USER_DISPLAY_NAME_LENGTH} and {MAX_USER_DISPLAY_NAME_LENGTH} characters long', + [StringNames.Validation_DisplayNameRequired]: 'Display name is required', + [StringNames.Validation_GameCodeRequired]: 'Game code is required', + [StringNames.Validation_GameNameRequired]: 'Game name is required', + [StringNames.Validation_GamePasswordRegexErrorTemplate]: + 'Game password must be between {MIN_GAME_PASSWORD_LENGTH} and {MAX_GAME_PASSWORD_LENGTH} characters long', [StringNames.Validation_InvalidEmail]: 'Invalid Email', + [StringNames.Validation_InvalidGame]: + 'Invalid game ID or game does not exist.', + [StringNames.Validation_InvalidGameCode]: 'Invalid Game Code', + [StringNames.Validation_InvalidGameCodeTemplate]: + 'Game code must be {GAME_CODE_LENGTH} characters long', + [StringNames.Validation_InvalidGameName]: 'Invalid Game Name', + [StringNames.Validation_InvalidGameNameTemplate]: + 'Game name must be between {MIN_GAME_NAME_LENGTH} and {MAX_GAME_NAME_LENGTH} characters long.', [StringNames.Validation_InvalidLanguage]: 'Invalid Language', + [StringNames.Validation_InvalidMessage]: 'Invalid Message', [StringNames.Validation_InvalidTimezone]: 'Invalid Timezone', [StringNames.Validation_InvalidToken]: 'Invalid Token', + [StringNames.Validation_InvalidMaxChefs]: 'Invalid number of chefs', + [StringNames.Validation_MaxChefsRequired]: 'Max chefs is required', + [StringNames.Validation_MessageRegexErrorTemplate]: + 'Message must be between {MIN_MESSAGE_LENGTH} and {MAX_MESSAGE_LENGTH} characters long.', [StringNames.Validation_PasswordRegexErrorTemplate]: `Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters, and contain at least: • One lowercase character (any script) • One uppercase character (any script) diff --git a/chili-and-cilantro-lib/src/lib/strings/english-us.ts b/chili-and-cilantro-lib/src/lib/strings/english-us.ts index 0f213d0..3a92a1a 100644 --- a/chili-and-cilantro-lib/src/lib/strings/english-us.ts +++ b/chili-and-cilantro-lib/src/lib/strings/english-us.ts @@ -51,8 +51,27 @@ export const AmericanEnglishStrings: StringsCollection = { [StringNames.Error_EmailTokenSentTooRecentlyTemplate]: 'Email token sent too recently. Please try again in {TIME_REMAINING} seconds.', [StringNames.Error_FailedToCreateEmailToken]: 'Failed to create email token', + [StringNames.Error_GameAlreadyInProgress]: 'Game is already in progress.', + [StringNames.Error_GameDisplayNameAlreadyInUse]: + 'Name is already in use within this game.', + [StringNames.Error_GameInvalidPhase]: + 'Game is not in the correct phase for this action.', + [StringNames.Error_GamePasswordMismatch]: 'Game password does not match.', + [StringNames.Error_InvalidAction]: 'Invalid action.', + [StringNames.Error_InvalidCredentials]: 'Invalid credentials.', + [StringNames.Error_MustBeMasterChef]: + 'You must be the master chef to perform this action.', + [StringNames.Error_NotEnoughChefsTemplate]: + 'Not enough chefs to start game. {CHEFS_PRESENT}/{MIN_CHEFS}', + [StringNames.Error_NotInGame]: 'You are not in this game.', + [StringNames.Error_NotYourTurn]: 'Not your turn.', + [StringNames.Error_OutOfIngredientTemplate]: 'Out of {INGREDIENT}.', [StringNames.Error_SendTokenFailure]: 'Failed to send email token', [StringNames.Error_TooManyChefs]: 'Too many chefs in the kitchen', + [StringNames.Error_UnexpectedTurnActionTemplate]: + 'Unexpected turn action: {TURN_ACTION}.', + [StringNames.Error_UsernameInUse]: 'Username is already in use.', + [StringNames.Error_UserNotFound]: 'User not found', [StringNames.Error_YouAlreadyJoined]: 'You have already joined this game', [StringNames.Dashboard_GamesCreated]: "Games You've Created", [StringNames.Dashboard_GamesParticipating]: "Games You're Participating in", @@ -103,10 +122,30 @@ export const AmericanEnglishStrings: StringsCollection = { 'In Chili and Cilantro, aspiring chefs compete to create the perfect dish. Your goal is to add just the right amount of cilantro without ruining it with a scorching chili. Be the first to successfully season two dishes or be the last chef standing to win!', [StringNames.Splash_HowToPlay]: 'How to Play', [StringNames.ValidationError]: 'Validation Error', + [StringNames.Validation_DisplayNameRegexErrorTemplate]: + 'User display name must be between {MIN_USER_DISPLAY_NAME_LENGTH} and {MAX_USER_DISPLAY_NAME_LENGTH} characters long', + [StringNames.Validation_DisplayNameRequired]: 'Display name is required', + [StringNames.Validation_GameCodeRequired]: 'Game code is required', + [StringNames.Validation_GameNameRequired]: 'Game name is required', + [StringNames.Validation_GamePasswordRegexErrorTemplate]: + 'Game password must be between {MIN_GAME_PASSWORD_LENGTH} and {MAX_GAME_PASSWORD_LENGTH} characters long', [StringNames.Validation_InvalidEmail]: 'Invalid Email', + [StringNames.Validation_InvalidGame]: + 'Invalid game ID or game does not exist.', + [StringNames.Validation_InvalidGameCode]: 'Invalid Game Code', + [StringNames.Validation_InvalidGameCodeTemplate]: + 'Game code must be {GAME_CODE_LENGTH} characters long', + [StringNames.Validation_InvalidGameName]: 'Invalid Game Name', + [StringNames.Validation_InvalidGameNameTemplate]: + 'Game name must be between {MIN_GAME_NAME_LENGTH} and {MAX_GAME_NAME_LENGTH} characters long.', [StringNames.Validation_InvalidLanguage]: 'Invalid Language', + [StringNames.Validation_InvalidMessage]: 'Invalid Message', [StringNames.Validation_InvalidTimezone]: 'Invalid Timezone', [StringNames.Validation_InvalidToken]: 'Invalid Token', + [StringNames.Validation_InvalidMaxChefs]: 'Invalid number of chefs', + [StringNames.Validation_MaxChefsRequired]: 'Max chefs is required', + [StringNames.Validation_MessageRegexErrorTemplate]: + 'Message must be between {MIN_MESSAGE_LENGTH} and {MAX_MESSAGE_LENGTH} characters long.', [StringNames.Validation_PasswordRegexErrorTemplate]: `Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters, and contain at least: • One lowercase character (any script) • One uppercase character (any script) diff --git a/chili-and-cilantro-lib/src/lib/strings/french.ts b/chili-and-cilantro-lib/src/lib/strings/french.ts index b4b6b6a..46acfc7 100644 --- a/chili-and-cilantro-lib/src/lib/strings/french.ts +++ b/chili-and-cilantro-lib/src/lib/strings/french.ts @@ -53,9 +53,29 @@ export const FrenchStrings: StringsCollection = { 'Le jeton de courrier électronique a été envoyé trop récemment. Veuillez réessayer dans {TIME_REMAINING} secondes.', [StringNames.Error_FailedToCreateEmailToken]: 'Échec de la création du jeton par courriel', + [StringNames.Error_GameAlreadyInProgress]: 'Le jeu est déjà en cours.', + [StringNames.Error_GameDisplayNameAlreadyInUse]: + 'Le nom est déjà utilisé dans ce jeu.', + [StringNames.Error_GameInvalidPhase]: + 'Le jeu n’est pas dans la phase correcte pour cette action.', + [StringNames.Error_GamePasswordMismatch]: + 'Le mot de passe du jeu ne correspond pas.', + [StringNames.Error_InvalidAction]: 'Action invalide', + [StringNames.Error_InvalidCredentials]: 'Identifiants invalides', + [StringNames.Error_MustBeMasterChef]: + 'Vous devez être le chef de cuisine pour effectuer cette action.', + [StringNames.Error_NotEnoughChefsTemplate]: + 'Pas assez de chefs pour commencer le jeu. {CHEFS_PRESENT}/{MIN_CHEFS}', + [StringNames.Error_NotInGame]: 'Vous n’êtes pas dans ce jeu.', + [StringNames.Error_NotYourTurn]: 'Ce n’est pas votre tour.', + [StringNames.Error_OutOfIngredientTemplate]: 'Plus de {INGREDIENT}.', [StringNames.Error_SendTokenFailure]: 'Échec de l’envoi du jeton par courriel', [StringNames.Error_TooManyChefs]: 'Trop de chefs dans la cuisine', + [StringNames.Error_UnexpectedTurnActionTemplate]: + 'Action de tour inattendue: {TURN_ACTION}.', + [StringNames.Error_UsernameInUse]: 'Nom d’utilisateur déjà utilisé', + [StringNames.Error_UserNotFound]: 'Utilisateur non trouvé', [StringNames.Error_YouAlreadyJoined]: 'Vous avez déjà rejoint ce jeu', [StringNames.Dashboard_GamesCreated]: 'Jeux que vous avez créé', [StringNames.Dashboard_GamesParticipating]: 'Jeux que vous participez', @@ -111,16 +131,34 @@ export const FrenchStrings: StringsCollection = { "Dans Chili and Cilantro, les chefs en herbe rivalisent pour créer le plat parfait. Votre objectif est d'ajouter juste la bonne quantité de coriandre sans gâcher le tout avec un piment brûlant. Soyez le premier à réussir à assaisonner deux plats ou soyez le dernier chef debout pour gagner !", [StringNames.Splash_HowToPlay]: 'Comment Jouer', [StringNames.ValidationError]: 'Erreur de validation', + [StringNames.Validation_DisplayNameRegexErrorTemplate]: + 'Le nom d’utilisateur doit contenir entre {MIN_USER_DISPLAY_NAME_LENGTH} et {MAX_USER_DISPLAY_NAME_LENGTH} caractères', + [StringNames.Validation_DisplayNameRequired]: 'Nom d’utilisateur requis', [StringNames.Validation_InvalidEmail]: 'Courriel invalide', + [StringNames.Validation_InvalidGameCode]: 'Code de jeu invalide', + [StringNames.Validation_InvalidGameCodeTemplate]: + 'Le code de jeu doit contenir {GAME_CODE_LENGTH} caractères', + [StringNames.Validation_GameCodeRequired]: 'Code de jeu requis', + [StringNames.Validation_GameNameRequired]: 'Nom du jeu requis', + [StringNames.Validation_GamePasswordRegexErrorTemplate]: + 'Le mot de passe du jeu doit contenir entre {MIN_GAME_PASSWORD_LENGTH} et {MAX_GAME_PASSWORD_LENGTH} caractères', + [StringNames.Validation_InvalidGame]: 'ID de jeu invalide ou jeu inexistant.', + [StringNames.Validation_InvalidGameName]: 'Nom de jeu invalide', + [StringNames.Validation_InvalidGameNameTemplate]: + 'Le nom du jeu doit contenir entre {MIN_GAME_NAME_LENGTH} et {MAX_GAME_NAME_LENGTH} caractères.', [StringNames.Validation_InvalidLanguage]: 'Langue invalide', + [StringNames.Validation_InvalidMessage]: 'Message invalide', [StringNames.Validation_InvalidTimezone]: 'Fuseau horaire invalide', [StringNames.Validation_InvalidToken]: 'Jeton invalide', + [StringNames.Validation_InvalidMaxChefs]: 'Nombre de chefs invalide', + [StringNames.Validation_MaxChefsRequired]: 'Le nombre de chefs est requis', + [StringNames.Validation_MessageRegexErrorTemplate]: + 'Le message doit contenir entre {MIN_MESSAGE_LENGTH} et {MAX_MESSAGE_LENGTH} caractères.', [StringNames.Validation_PasswordRegexErrorTemplate]: `Le mot de passe doit contenir entre {MIN_PASSWORD_LENGTH} et {MAX_PASSWORD_LENGTH} caractères, et contenir au moins : • Une lettre minuscule (tout script) • Une lettre majuscule (tout script) • Un chiffre (tout système numérique) • Un caractère spécial (ponctuation ou symbole)`, - [StringNames.Validation_CurrentPasswordRequired]: 'Le mot de passe actuel est requis', [StringNames.Validation_PasswordsDifferent]: diff --git a/chili-and-cilantro-lib/src/lib/strings/mandarin.ts b/chili-and-cilantro-lib/src/lib/strings/mandarin.ts index 6a6e7ac..9f71f58 100644 --- a/chili-and-cilantro-lib/src/lib/strings/mandarin.ts +++ b/chili-and-cilantro-lib/src/lib/strings/mandarin.ts @@ -47,8 +47,24 @@ export const MandarinStrings: StringsCollection = { [StringNames.Error_EmailTokenSentTooRecentlyTemplate]: '电子邮件令牌发送太频繁。请在{TIME_REMAINING}秒后重试。', [StringNames.Error_FailedToCreateEmailToken]: '无法创建电子邮件令牌', + [StringNames.Error_GameAlreadyInProgress]: '游戏已经开始。', + [StringNames.Error_GameDisplayNameAlreadyInUse]: '游戏名称已经在使用中', + [StringNames.Error_GameInvalidPhase]: '游戏不处于此操作的正确阶段。', + [StringNames.Error_GamePasswordMismatch]: '游戏密码不匹配', + [StringNames.Error_InvalidAction]: '无效操作', + [StringNames.Error_InvalidCredentials]: '无效凭证', + [StringNames.Error_MustBeMasterChef]: '您必须是大厨才能执行此操作。', + [StringNames.Error_NotEnoughChefsTemplate]: + '没有足够的厨师开始游戏。{CHEFS_PRESENT}/{MIN_CHEFS}', + [StringNames.Error_NotInGame]: '您不在这个游戏中', + [StringNames.Error_NotYourTurn]: '不是你的回合', + [StringNames.Error_OutOfIngredientTemplate]: '缺少{INGREDIENT}', [StringNames.Error_SendTokenFailure]: '无法发送电子邮件令牌', [StringNames.Error_TooManyChefs]: '厨房里有太多厨师', + [StringNames.Error_UnexpectedTurnActionTemplate]: + '意外的回合动作:{TURN_ACTION}', + [StringNames.Error_UsernameInUse]: '用户名已经在使用中', + [StringNames.Error_UserNotFound]: '用户未找到', [StringNames.Error_YouAlreadyJoined]: '您已经加入该游戏', [StringNames.Dashboard_GamesCreated]: '您创建的游戏', [StringNames.Dashboard_GamesParticipating]: '您参与的游戏', @@ -94,10 +110,29 @@ export const MandarinStrings: StringsCollection = { '在《辣椒和香菜》中,有抱负的厨师们竞相制作完美的菜肴。你的目标是添加适量的香菜,而不会让辣椒烧焦。成为第一个成功调味两道菜的人,或者成为最后一个获胜的厨师', [StringNames.Splash_HowToPlay]: '如何玩', [StringNames.ValidationError]: '验证错误', + [StringNames.Validation_DisplayNameRegexErrorTemplate]: + '用户显示名称必须在{MIN_USER_DISPLAY_NAME_LENGTH}和{MAX_USER_DISPLAY_NAME_LENGTH}个字符之间', + [StringNames.Validation_DisplayNameRequired]: '昵称是必需的', + [StringNames.Validation_GameCodeRequired]: '游戏代码是必需的', + [StringNames.Validation_GameNameRequired]: '游戏名是必需的', + [StringNames.Validation_GamePasswordRegexErrorTemplate]: + '游戏密码必须在{MIN_GAME_PASSWORD_LENGTH}和{MAX_GAME_PASSWORD_LENGTH}个字符之间', [StringNames.Validation_InvalidEmail]: '无效电子邮件', + [StringNames.Validation_InvalidGame]: '无效游戏ID或游戏不存在。', + [StringNames.Validation_InvalidGameCode]: '无效游戏代码', + [StringNames.Validation_InvalidGameCodeTemplate]: + '游戏代码必须是{GAME_CODE_LENGTH}个字符长', + [StringNames.Validation_InvalidGameName]: '无效游戏名称', + [StringNames.Validation_InvalidGameNameTemplate]: + '游戏名称必须在{MIN_GAME_NAME_LENGTH}和{MAX_GAME_NAME_LENGTH}个字符之间。', [StringNames.Validation_InvalidLanguage]: '无效语言', + [StringNames.Validation_InvalidMessage]: '无效消息', + [StringNames.Validation_InvalidMaxChefs]: '无效厨师数量', [StringNames.Validation_InvalidTimezone]: '无效时区', [StringNames.Validation_InvalidToken]: '无效令牌', + [StringNames.Validation_MaxChefsRequired]: '最大厨师是必需的', + [StringNames.Validation_MessageRegexErrorTemplate]: + '消息必须在{MIN_MESSAGE_LENGTH}和{MAX_MESSAGE_LENGTH}个字符之间。', [StringNames.Validation_PasswordRegexErrorTemplate]: `密码必须在{MIN_PASSWORD_LENGTH}和{MAX_PASSWORD_LENGTH}个字符之间,并且至少包含: • 一个小写字母(任何脚本) • 一个大写字母(任何脚本) diff --git a/chili-and-cilantro-lib/src/lib/strings/spanish.ts b/chili-and-cilantro-lib/src/lib/strings/spanish.ts index 266222c..b9eac97 100644 --- a/chili-and-cilantro-lib/src/lib/strings/spanish.ts +++ b/chili-and-cilantro-lib/src/lib/strings/spanish.ts @@ -54,8 +54,28 @@ export const SpanishStrings: StringsCollection = { 'El token de correo electrónico se envió hace muy poco. Inténtalo de nuevo en {TIME_REMAINING} segundos.', [StringNames.Error_FailedToCreateEmailToken]: 'Fallo al crear el token de correo', + [StringNames.Error_GameAlreadyInProgress]: 'El juego ya está en progreso.', + [StringNames.Error_GameDisplayNameAlreadyInUse]: + 'El nombre ya está en uso en este juego.', + [StringNames.Error_GameInvalidPhase]: + 'El juego no está en la fase correcta para esta acción.', + [StringNames.Error_GamePasswordMismatch]: + 'La contraseña del juego no coincide.', + [StringNames.Error_InvalidAction]: 'Acción inválida.', + [StringNames.Error_InvalidCredentials]: 'Credenciales inválidas.', + [StringNames.Error_MustBeMasterChef]: + 'Debes ser el chef maestro para realizar esta acción.', + [StringNames.Error_NotEnoughChefsTemplate]: + 'No hay suficientes chefs para comenzar el juego. {CHEFS_PRESENT}/{MIN_CHEFS}', + [StringNames.Error_NotInGame]: 'No estás en este juego.', + [StringNames.Error_NotYourTurn]: 'No es tu turno.', + [StringNames.Error_OutOfIngredientTemplate]: 'Sin {INGREDIENT}.', [StringNames.Error_SendTokenFailure]: 'Fallo al enviar el token de correo', [StringNames.Error_TooManyChefs]: 'Demasiados chefs en la cocina', + [StringNames.Error_UnexpectedTurnActionTemplate]: + 'Acción de turno inesperada: {TURN_ACTION}.', + [StringNames.Error_UsernameInUse]: 'Nombre de usuario en uso', + [StringNames.Error_UserNotFound]: 'Usuario no encontrado', [StringNames.Error_YouAlreadyJoined]: 'Ya te has unido a este juego', [StringNames.Dashboard_GamesCreated]: 'Juegos que has creado', [StringNames.Dashboard_GamesParticipating]: 'Juegos en los que participas', @@ -106,10 +126,31 @@ export const SpanishStrings: StringsCollection = { 'En Chili and Cilantro, los aspirantes a chef compiten para crear el plato perfecto. Tu objetivo es agregar la cantidad justa de cilantro sin arruinarlo con un chile abrasador. ¡Sé el primero en condimentar con éxito dos platos o sé el último chef en pie para ganar!', [StringNames.Splash_HowToPlay]: 'Como Jugar', [StringNames.ValidationError]: 'Error de validación', + [StringNames.Validation_DisplayNameRegexErrorTemplate]: + 'El nombre de usuario debe tener entre {MIN_USER_DISPLAY_NAME_LENGTH} y {MAX_USER_DISPLAY_NAME_LENGTH} caracteres', + [StringNames.Validation_DisplayNameRequired]: + 'Se requiere un nombre de usuario', + [StringNames.Validation_GameCodeRequired]: 'Se requiere un código de juego', + [StringNames.Validation_GameNameRequired]: 'Se requiere un nombre de juego', + [StringNames.Validation_GamePasswordRegexErrorTemplate]: + 'La contraseña del juego debe tener entre {MIN_GAME_PASSWORD_LENGTH} y {MAX_GAME_PASSWORD_LENGTH} caracteres', [StringNames.Validation_InvalidEmail]: 'Correo electrónica inválido', + [StringNames.Validation_InvalidGame]: + 'ID de juego inválido o juego no existe.', + [StringNames.Validation_InvalidGameCode]: 'Código de juego invático', + [StringNames.Validation_InvalidGameCodeTemplate]: + 'El código del juego debe tener {GAME_CODE_LENGTH} caracteres', + [StringNames.Validation_InvalidGameName]: 'Nombre de juego invático', + [StringNames.Validation_InvalidGameNameTemplate]: + 'El nombre del juego debe tener entre {MIN_GAME_NAME_LENGTH} y {MAX_GAME_NAME_LENGTH} caracteres.', [StringNames.Validation_InvalidLanguage]: 'Idioma inválido', + [StringNames.Validation_InvalidMessage]: 'Mensaje inválido', + [StringNames.Validation_InvalidMaxChefs]: 'Número inválido de chefs', [StringNames.Validation_InvalidTimezone]: 'Fuseau horaire invático', [StringNames.Validation_InvalidToken]: 'Token inválido', + [StringNames.Validation_MaxChefsRequired]: 'Se requiere un número de chefs', + [StringNames.Validation_MessageRegexErrorTemplate]: + 'El mensaje debe tener entre {MIN_MESSAGE_LENGTH} y {MAX_MESSAGE_LENGTH} caracteres.', [StringNames.Validation_PasswordRegexErrorTemplate]: `La contraseña debe estar entre {MIN_PASSWORD_LENGTH} y {MAX_PASSWORD_LENGTH} caracteres, y contener al menos: • Una letra minuscula (cualquier script) • Una letra mayúscula (cualquier script) diff --git a/chili-and-cilantro-lib/src/lib/strings/ukrainian.ts b/chili-and-cilantro-lib/src/lib/strings/ukrainian.ts index ee7dee7..a6d09be 100644 --- a/chili-and-cilantro-lib/src/lib/strings/ukrainian.ts +++ b/chili-and-cilantro-lib/src/lib/strings/ukrainian.ts @@ -52,9 +52,28 @@ export const UkrainianStrings: StringsCollection = { 'Маркер електронної пошти надіслано занадто недавно. Повторіть спробу через {TIME_REMAINING} с.', [StringNames.Error_FailedToCreateEmailToken]: 'Неможливо створити електронний токен', + [StringNames.Error_GameAlreadyInProgress]: 'Гра вже розпочалася', + [StringNames.Error_GameDisplayNameAlreadyInUse]: + 'Ім’я вже використовується в цій грі.', + [StringNames.Error_GameInvalidPhase]: + 'Гра не знаходиться в правильній фазі для цієї дії.', + [StringNames.Error_GamePasswordMismatch]: 'Пароль гри не відповідає', + [StringNames.Error_InvalidAction]: 'Недійсна дія', + [StringNames.Error_InvalidCredentials]: 'Неправильні дані', + [StringNames.Error_MustBeMasterChef]: + 'Ви повинні бути майстром-кухарем, щоб виконати цю дію.', + [StringNames.Error_NotEnoughChefsTemplate]: + 'Недостатньо шеф-кухарів для початку гри. {CHEFS_PRESENT}/{MIN_CHEFS}', + [StringNames.Error_NotInGame]: 'Ви не в цій грі', + [StringNames.Error_NotYourTurn]: 'Це не ваш ход', + [StringNames.Error_OutOfIngredientTemplate]: 'Недостатньо {INGREDIENT}.', [StringNames.Error_SendTokenFailure]: 'Неможливо відправити електронний токен', [StringNames.Error_TooManyChefs]: 'Забагато шеф-кухарів на кухні', + [StringNames.Error_UnexpectedTurnActionTemplate]: + 'Неочікувана дія в ході: {TURN_ACTION}', + [StringNames.Error_UsernameInUse]: 'Ім’я користувача вже використовується', + [StringNames.Error_UserNotFound]: 'Користувача не знайдено', [StringNames.Error_YouAlreadyJoined]: 'Ви вже приєдналися до цієї гри', [StringNames.Dashboard_GamesCreated]: 'Гри, які ти створив', [StringNames.Dashboard_GamesParticipating]: 'Гри, в яких ти береш участь', @@ -107,10 +126,32 @@ export const UkrainianStrings: StringsCollection = { 'У Chili and Cilantro починаючі кухарі змагаються, щоб створити ідеальну страву. Ваша мета — додати потрібну кількість кінзи, не зіпсувавши її пекучим чилі. Будьте першим, хто успішно приправить дві страви, або будьте останнім шеф-кухарем, який виграє!', [StringNames.Splash_HowToPlay]: 'Як Грати', [StringNames.ValidationError]: 'Помилка валідації', + [StringNames.Validation_DisplayNameRegexErrorTemplate]: + 'Ім’я користувача повинно бути від {MIN_USER_DISPLAY_NAME_LENGTH} до {MAX_USER_DISPLAY_NAME_LENGTH} символів', + [StringNames.Validation_DisplayNameRequired]: + 'Ім’я користувача є обов’язковим', + [StringNames.Validation_GameCodeRequired]: 'Код гри є обов’язковим', + [StringNames.Validation_GameNameRequired]: 'Ім’я гри є обов’язковим', + [StringNames.Validation_GamePasswordRegexErrorTemplate]: + 'Пароль гри повинен бути від {MIN_GAME_PASSWORD_LENGTH} до {MAX_GAME_PASSWORD_LENGTH} символів', [StringNames.Validation_InvalidEmail]: 'Недійсний електронній адрес', + [StringNames.Validation_InvalidGame]: + 'Недійсний ідентифікатор гри або гра не існує.', + [StringNames.Validation_InvalidGameCode]: 'Недійсний код гри', + [StringNames.Validation_InvalidGameCodeTemplate]: + 'Код гри повинен бути {GAME_CODE_LENGTH} символів', + [StringNames.Validation_InvalidGameName]: 'Недійсне ім’я гри', + [StringNames.Validation_InvalidGameNameTemplate]: + 'Ім’я гри повинно бути від {MIN_GAME_NAME_LENGTH} до {MAX_GAME_NAME_LENGTH} символів.', [StringNames.Validation_InvalidLanguage]: 'Недійсна мова', + [StringNames.Validation_InvalidMaxChefs]: 'Недійсна кількість шеф-кухарів', + [StringNames.Validation_InvalidMessage]: 'Недійсне повідомлення', [StringNames.Validation_InvalidTimezone]: 'Недійсна часова зона', [StringNames.Validation_InvalidToken]: 'Недійсний токен', + [StringNames.Validation_MaxChefsRequired]: + 'Максимальна кількість шеф-кухарів є обов’язковою', + [StringNames.Validation_MessageRegexErrorTemplate]: + 'Повідомлення повинно бути від {MIN_MESSAGE_LENGTH} до {MAX_MESSAGE_LENGTH} символів.', [StringNames.Validation_PasswordRegexErrorTemplate]: `Пароль повинен бути від {MIN_PASSWORD_LENGTH} до {MAX_PASSWORD_LENGTH} символів, та містити як мінімум: • Одну нижню літеру (всіх скриптів) • Одну верхню літеру (всіх скриптів) diff --git a/chili-and-cilantro-react/src/app/components/game.tsx b/chili-and-cilantro-react/src/app/components/game.tsx index ead3d5f..eae4427 100644 --- a/chili-and-cilantro-react/src/app/components/game.tsx +++ b/chili-and-cilantro-react/src/app/components/game.tsx @@ -38,18 +38,22 @@ const GameSetup: React.FC = () => { const validationSchema = Yup.object().shape({ gameName: Yup.string().test( 'gameName', - 'Invalid game name', + t(StringNames.Validation_InvalidGameName), function (value) { const { gameMode } = this.parent; if (gameMode === 'CREATE') { if (!value) - return this.createError({ message: 'Game name is required' }); + return this.createError({ + message: t(StringNames.Validation_GameNameRequired), + }); if ( - !constants.MULTILINGUAL_STRING_REGEX.test(value) || + !constants.GAME_NAME_REGEX.test(value) || value.length < constants.MIN_GAME_NAME_LENGTH || value.length > constants.MAX_GAME_NAME_LENGTH ) { - return this.createError({ message: 'Invalid game name' }); + return this.createError({ + message: t(StringNames.Validation_InvalidGameNameTemplate), + }); } } return true; @@ -57,15 +61,17 @@ const GameSetup: React.FC = () => { ), gameCode: Yup.string().test( 'gameCode', - 'Invalid game code', + t(StringNames.Validation_InvalidGameCode), function (value) { const { gameMode } = this.parent; if (gameMode === 'JOIN') { if (!value) - return this.createError({ message: 'Game code is required' }); + return this.createError({ + message: t(StringNames.Validation_GameCodeRequired), + }); if (!constants.GAME_CODE_REGEX.test(value)) { return this.createError({ - message: constants.GAME_CODE_REGEX_ERROR, + message: t(StringNames.Validation_InvalidGameCodeTemplate), }); } } @@ -75,25 +81,29 @@ const GameSetup: React.FC = () => { displayname: Yup.string() .matches( constants.USER_DISPLAY_NAME_REGEX, - constants.USER_DISPLAY_NAME_REGEX_ERROR, + t(StringNames.Validation_DisplayNameRegexErrorTemplate), ) - .required('Display name is required'), + .required(t(StringNames.Validation_DisplayNameRequired)), gamePassword: Yup.string() .matches( constants.GAME_PASSWORD_REGEX, - constants.GAME_PASSWORD_REGEX_ERROR, + t(StringNames.Validation_GamePasswordRegexErrorTemplate), ) .optional(), maxChefs: Yup.number().test( 'maxChefs', - 'Invalid number of chefs', + t(StringNames.Validation_InvalidMaxChefs), function (value) { const { gameMode } = this.parent; if (gameMode === 'CREATE') { if (!value) - return this.createError({ message: 'Max chefs is required' }); + return this.createError({ + message: t(StringNames.Validation_MaxChefsRequired), + }); if (value < constants.MIN_CHEFS || value > constants.MAX_CHEFS) { - return this.createError({ message: 'Invalid number of chefs' }); + return this.createError({ + message: t(StringNames.Validation_InvalidMaxChefs), + }); } } return true; @@ -101,7 +111,7 @@ const GameSetup: React.FC = () => { ), gameMode: Yup.string() .oneOf(['CREATE', 'JOIN']) - .required('Game mode is required'), + .required(t(StringNames.Validation_Required)), }); const formik = useFormik({ diff --git a/chili-and-cilantro-react/src/app/components/register-page.tsx b/chili-and-cilantro-react/src/app/components/register-page.tsx index c1a0c2e..3503aa8 100644 --- a/chili-and-cilantro-react/src/app/components/register-page.tsx +++ b/chili-and-cilantro-react/src/app/components/register-page.tsx @@ -52,7 +52,7 @@ const RegisterPage: React.FC = () => { displayname: Yup.string() .matches( constants.USER_DISPLAY_NAME_REGEX, - constants.USER_DISPLAY_NAME_REGEX_ERROR, + t(StringNames.Validation_DisplayNameRegexErrorTemplate), ) .required(t(StringNames.Validation_Required)), email: Yup.string() @@ -63,7 +63,7 @@ const RegisterPage: React.FC = () => { constants.PASSWORD_REGEX, t(StringNames.Validation_PasswordRegexErrorTemplate), ) - .required('Required'), + .required(t(StringNames.Validation_Required)), }), onSubmit: async (values, { setSubmitting }) => { try {