class-validator-translator-middleware

A middleware to convert class-validator messages to locale languages using similar i18n pattern

Usage no npm install needed!

<script type="module">
  import classValidatorTranslatorMiddleware from 'https://cdn.skypack.dev/class-validator-translator-middleware';
</script>

README

class-validator-translator-middleware

How to use this package

  • first install it npm i class-validator-translator-middleware

  • Now create a directory wherever you want and name it what ever you like but I use class-validator-errors: mkdir src/class-validator-errors

  • Then you should create error-codes.json in that directory: touch src/class-validator-errors/error-codes.json

  • Now in it you have to define your error codes like this.

    {
        "title_should_be_sample": "title_should_be_sample"
    }
    
  • Now you must define your error locale messages in separated files on that directory. E.X. you should touch src/class-validator-errors/en.json and in that file you have:

    {
        "title_should_be_sample": "sample is good"
    }
    

    And Also touch src/class-validator-errors/fa.json:

    {
        "title_should_be_sample": "تست خوبه"
    }
    

    Important note: You cannot define an error code in error-codes.json but it had no defined error message in your error locale messages. If you do this intentionally or accidentally you will get an error while creating a new instance of ClassValidatorTranslatorMiddleware, The thrown error is instance of this error custom error class: IncompatibleErrorCodeAndErrorMessage.

    Since I am reading the error messages using require, they will be cached in the memory and this will prevent unnecessary reads from file system. for more info please read ClassValidatorTranslatorMiddleware codes :star_struck:

    Note if you are using tsc: because we will never ever use fa.json and other json files you have to include this unused json files in your tsconfig.json/tsconfig.build.json file like this (If you do not include your fa.json, en.json, ... files will not be in the compiled version. unless you import them implicitly):

    {
        "include": ["src", "src/**/*.json"]
        // ...
    }
    

    I client side sends an locale that you do not defined its error-message file in the class-validator-errors directory you will get An error in the next error handler middleware. In this case you will get an error that it is instance of SpecifiedErrorMessageFileNotFound.

  • Your frontend have to specify the accept-language header in their requests and it should be within the Locale enum.

  • Final examples:

    • Here is one example in ExpressJS:

      // app.ts
      import { join } from 'path';
      import { Equals, IsOptional } from 'class-validator';
      import {
          ClassValidatorTranslatorMiddleware,
          IncompatibleErrorCodeAndErrorMessage
      } from 'class-validator-translator-middleware';
      
      process.on('uncaughtException', (error) => {
          if (error instanceof IncompatibleErrorCodeAndErrorMessage) {
              // do what ever you like, like sending email/notification to your devops guy
              process.exit(1);
          }
      })
      
      const messagesPath = join(__dirname, 'class-validator-errors');
      const classValidatorTranslatorMiddleware =
          new ClassValidatorTranslatorMiddleware(messagesPath);
      
      
          const classValidatorTranslatorMiddleware =
              new ClassValidatorTranslatorMiddleware(messagesPath);
      
          app.use(classValidatorTranslatorMiddleware.middleware);
      
          // this can be your class validator
          class TestClassValidator {
              @IsOptional()
              @Equals('sample', { message: 'title_should_be_sample' })
              title: string = 'bad_value';
          }
      
          app.use((error, req, res, next) => {
              for (const error of errors) {
                  console.log(error.constraints); // تست خوبه
              }
          });
          ```
      
      
      
      
      
      
    • Here is one example in routing controller

      // messages.ts
      import { ClassValidatorTranslatorMiddleware } from 'class-validator-translator-middleware';
      
      // city-filter.ts
      import {
          IsBoolean,
          IsAlpha,
          IsOptional,
      } from 'class-validator';
      
      export class CityFilterQuery {
          @IsOptional()
          @IsAlpha('en', {
              message: 'serviced_should_be_boolean_not_empty',
          })
          @IsBoolean({ message: 'serviced_should_be_boolean' })
          serviced?: boolean;
      }
      
      // class-validator-error-translator.middleware.ts
      import { join } from 'path';
      import { ValidationError } from 'class-validator';
      import {
          Middleware,
          ExpressErrorMiddlewareInterface,
          HttpError,
      } from 'routing-controllers';
      import { NextFunction, Request, Response } from 'express';
      import { ClassValidatorTranslatorMiddleware } from 'class-validator-translator-middleware';
      
      const messagesPath = join(
          __dirname,
          'class-validator-errors',
      );
      const classValidatorTranslatorMiddleware =
          new ClassValidatorTranslatorMiddleware(messagesPath);
      
      class CustomValidationError {
          errors: ValidationError[];
          status?: number;
          statusCode?: number;
      }
      
      @Middleware({ type: 'after' })
      export class ClassValidatorErrorTranslator
          implements ExpressErrorMiddlewareInterface
      {
          error(
              error: HttpError | CustomValidationError,
              request: Request,
              response: Response,
              next: NextFunction,
          ): void {
              classValidatorTranslatorMiddleware.middleware(
                  error,
                  request,
                  response,
                  next,
              );
          }
      }
      
      // another middleware
      import { ValidationError } from 'class-validator';
      import {
          Middleware,
          ExpressErrorMiddlewareInterface,
          HttpError,
      } from 'routing-controllers';
      import { NextFunction, Request, Response } from 'express';
      import { SpecifiedErrorMessageFileNotFound } from 'class-validator-translator-middleware';
      
      @Middleware({ type: 'after' })
      export class ClassValidatorErrorTranslator
          implements ExpressErrorMiddlewareInterface
      {
          error(
              error: HttpError | CustomValidationError,
              request: Request,
              response: Response,
              next: NextFunction,
          ): void {
              if (
                  error instanceof SpecifiedErrorMessageFileNotFound
              ) {
                  // do whatever you want.
                  response.status(400).json({
                      message: 'un-supported-language',
                  });
              }
              next(error);
          }
      }
      
      // app.ts
      import { ClassValidatorErrorTranslator } from './middlewares/class-validator-error-translator.middleware';
      import express from 'express';
      
      const app = express();
      
      const routingControllersOptions: RoutingControllersOptions = {
          controllers: [
              /* controllers */
          ],
          middlewares: [ClassValidatorErrorTranslator],
          /* other configs */
          validation: {
              whitelist: true,
          },
      };
      useExpressServer(app, routingControllersOptions);
      

Supported languages in Locale enum

Table 1. Language Codes
Language Code
Abkhazian AB
Afar AA
Afrikaans AF
Albanian SQ
Amharic AM
Arabic AR
Armenian HY
Assamese AS
Aymara AY
Azerbaijani AZ
Bashkir BA
Basque EU
Bengali, Bangla BN
Bhutani DZ
Bihari BH
Bislama BI
Breton BR
Bulgarian BG
Burmese MY
Byelorussian BE
Cambodian KM
Catalan CA
Chinese ZH
Corsican CO
Croatian HR
Czech CS
Danish DA
Dutch NL
English, American EN
Esperanto EO
Estonian ET
Faeroese FO
Fiji FJ
Finnish FI
French FR
Frisian FY
Gaelic (Scots Gaelic) GD
Galician GL
Georgian KA
German DE
Greek EL
Greenlandic KL
Guarani GN
Gujarati GU
Hausa HA
Hebrew IW
Hindi HI
Hungarian HU
Icelandic IS
Indonesian IN
Interlingua IA
Interlingue IE
Inupiak IK
Irish GA
Italian IT
Japanese JA
Javanese JW
Kannada KN
Kashmiri KS
Kazakh KK
Kinyarwanda RW
Kirghiz KY
Kirundi RN
Korean KO
Kurdish KU
Laothian LO
Latin LA
Latvian, Lettish LV
Lingala LN
Lithuanian LT
Macedonian MK
Malagasy MG
Malay MS
Malayalam ML
Maltese MT
Maori MI
Marathi MR
Moldavian MO
Mongolian MN
Nauru NA
Nepali NE
Norwegian NO
Occitan OC
Oriya OR
Oromo, Afan OM
Pashto, Pushto PS
Persian FA
Polish PL
Portuguese PT
Punjabi PA
Quechua QU
Rhaeto-Romance RM
Romanian RO
Russian RU
Samoan SM
Sangro SG
Sanskrit SA
Serbian SR
Serbo-Croatian SH
Sesotho ST
Setswana TN
Shona SN
Sindhi SD
Singhalese SI
Siswati SS
Slovak SK
Slovenian SL
Somali SO
Spanish ES
Sudanese SU
Swahili SW
Swedish SV
Tagalog TL
Tajik TG
Tamil TA
Tatar TT
Tegulu TE
Thai TH
Tibetan BO
Tigrinya TI
Tonga TO
Tsonga TS
Turkish TR
Turkmen TK
Twi TW
Ukrainian UK
Urdu UR
Uzbek UZ
Vietnamese VI
Volapuk VO
Welsh CY
Wolof WO
Xhosa XH
Yiddish JI
Yoruba YO
Zulu ZU

Notes

  • I had no time to add all the languages in the Locale
  • Please use it with Typescript to prevent any unexpected behaviours
  • Feel free to create issue or pull request

Contribution

  • Clone repository
  • npm i; npm i prepeare
  • Create a new branch from develop branch (git checkout -b develop-your-name)
  • Write you code
  • Write its tests in tests directory
  • run npm test and make sure it works perfectly