Repository: validatorjs/validator.js
Pull request #1657 (2021-04-25 18:54:26)

feat(isIdentityCard): add TH (Thai)
Add support for Thai National ID (TH) in isIdentityCard 

## Checklist

- [x] PR contains only changes related; no stray files, etc.
- [x] README updated (where applicable)
- [x] Tests written (where applicable)


codecov[bot]: # [Codecov](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) Report
> Merging [#1657](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) (05e148c) into [master](https://codecov.io/gh/validatorjs/validator.js/commit/d1a9b6d8c5bd7350d6a7303085f0269f6a99aa9b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) (d1a9b6d) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/validatorjs/validator.js/pull/1657/graphs/tree.svg?width=650&height=150&src=pr&token=YzR3kbN1m2&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs)](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs)

```diff
@@            Coverage Diff            @@
##            master     #1657   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          101       101           
  Lines         1854      1859    +5     
=========================================
+ Hits          1854      1859    +5     
```


| [Impacted Files](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) | Coverage Δ | |
|---|---|---|
| [src/lib/isIdentityCard.js](https://codecov.io/gh/validatorjs/validator.js/pull/1657/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs#diff-c3JjL2xpYi9pc0lkZW50aXR5Q2FyZC5qcw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs). Last update [d1a9b6d...05e148c](https://codecov.io/gh/validatorjs/validator.js/pull/1657?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index efa41e1..21686d8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,11 +23,8 @@ jobs:
       run: npm install
     - name: Run tests
       run: npm test
-    - if: matrix.node-version == 14
-      name: Generate coverage file
-      run: npm run test:ci > coverage.lcov
     - if: matrix.node-version == 14
       name: Send coverage info to Codecov
       uses: codecov/codecov-action@v1
       with:
-        file: ./coverage.lcov
+        file: ./coverage/cobertura-coverage.xml
diff --git a/README.md b/README.md
index 7dbed12..25af675 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ Validator                               | Description
 **isBase64(str [, options])**          | check if a string is base64 encoded. options is optional and defaults to `{urlSafe: false}`<br/> when `urlSafe` is true it tests the given base64 encoded string is [url safe](https://base64.guru/standards/base64url)
 **isBefore(str [, date])**              | check if the string is a date that's before the specified date.
 **isBIC(str)**                          | check if a string is a BIC (Bank Identification Code) or SWIFT code.
-**isBoolean(str)**                      | check if a string is a boolean.
+**isBoolean(str [, options])**          | check if a string is a boolean.<br/>`options` is an object which defaults to `{ loose: false }`. If loose is is set to false, the validator will strictly match ['true', 'false', '0', '1']. If loose is set to true, the validator will also match 'yes', 'no', and will match a valid boolean string of any case. (eg: ['true', 'True', 'TRUE']).
 **isBtcAddress(str)**            | check if the string is a valid BTC address.
 **isByteLength(str [, options])**          | check if the string's length (in UTF-8 bytes) falls in a range.<br/><br/>`options` is an object which defaults to `{min:0, max: undefined}`.
 **isCreditCard(str)**                   | check if the string is a credit card.
@@ -115,7 +115,7 @@ Validator                               | Description
 **isHexColor(str)**                     | check if the string is a hexadecimal color.
 **isHSL(str)**                          | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value).<br/><br/>Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: `hsl(200grad+.1%62%/1)`).
 **isIBAN(str)**                         | check if a string is a IBAN (International Bank Account Number).
-**isIdentityCard(str [, locale])**      | check if the string is a valid identity card code.<br/><br/>`locale` is one of `['ES', 'IN', 'IT', 'IR', 'MZ', 'NO', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN']` OR `'any'`. If 'any' is used, function will check if any of the locals match.<br/><br/>Defaults to 'any'.
+**isIdentityCard(str [, locale])**      | check if the string is a valid identity card code.<br/><br/>`locale` is one of `['ES', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN']` OR `'any'`. If 'any' is used, function will check if any of the locals match.<br/><br/>Defaults to 'any'.
 **isIMEI(str [, options]))**                         | check if the string is a valid IMEI number. Imei should be of format `###############` or `##-######-######-#`.<br/><br/>`options` is an object which can contain the keys `allow_hyphens`. Defaults to first format . If allow_hyphens is set to true, the validator will validate the second format.
 **isIn(str, values)**                   | check if the string is in a array of allowed values.
 **isInt(str [, options])**              | check if the string is an integer.<br/><br/>`options` is an object which can contain the keys `min` and/or `max` to check the integer is within boundaries (e.g. `{ min: 10, max: 99 }`). `options` can also contain the key `allow_leading_zeroes`, which when set to false will disallow integer values with leading zeroes (e.g. `{ allow_leading_zeroes: false }`). Finally, `options` can contain the keys `gt` and/or `lt` which will enforce integers being greater than or less than, respectively, the value provided (e.g. `{gt: 1, lt: 4}` for a number between 1 and 4).
@@ -132,7 +132,7 @@ Validator                               | Description
 **isJWT(str)**                         | check if the string is valid JWT token.
 **isLatLong(str [, options])**                      | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.<br/><br/>`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
 **isLength(str [, options])**              | check if the string's length falls in a range.<br/><br/>`options` is an object which defaults to `{min:0, max: undefined}`. Note: this function takes into account surrogate pairs.
-**isLicensePlate(str [, locale])**     | check if string matches the format of a country's license plate.<br/><br/>(locale is one of `['de-DE', 'de-LI', 'pt-PT', 'sq-AL', 'pt-BR'']` or `any`).
+**isLicensePlate(str [, locale])**     | check if string matches the format of a country's license plate.<br/><br/>(locale is one of `['cs-CZ', 'de-DE', 'de-LI', 'pt-PT', 'sq-AL', 'pt-BR']` or `any`)
 **isLocale(str)**                       | check if the string is a locale
 **isLowercase(str)**                    | check if the string is lowercase.
 **isMACAddress(str)**                   | check if the string is a MAC address.<br/><br/>`options` is an object which defaults to `{no_separators: false}`. If `no_separators` is true, the validator will allow MAC addresses without separators. Also, it allows the use of hyphens, spaces or dots e.g  '01 02 03 04 05 ab', '01-02-03-04-05-ab' or '0102.0304.05ab'.
@@ -144,7 +144,7 @@ Validator                               | Description
 **isMultibyte(str)**                    | check if the string contains one or more multibyte chars.
 **isNumeric(str [, options])**                      | check if the string contains only numbers.<br/><br/>`options` is an object which defaults to `{no_symbols: false}` it also has locale as an option. If `no_symbols` is true, the validator will reject numeric strings that feature a symbol (e.g. `+`, `-`, or `.`).<br/><br/>`locale` determine the decimal separator and is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fr-FR', 'fr-CA', 'hu-HU', 'it-IT', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`.
 **isOctal(str)**                        | check if the string is a valid octal number.
-**isPassportNumber(str, countryCode)**    | check if the string is a valid passport number.<br/><br/>(countryCode is one of `[ 'AM', 'AR', 'AT', 'AU', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE' 'IN', 'IR', 'IS', 'IT', 'JP', 'KR', 'LT', 'LU', 'LV', 'LY', 'MT', 'MY', 'MZ', 'NL', 'PO', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TR', 'UA', 'US' ]`.
+**isPassportNumber(str, countryCode)**    | check if the string is a valid passport number.<br/><br/>(countryCode is one of `[ 'AM', 'AR', 'AT', 'AU', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE' 'IN', 'IR', 'ID', 'IS', 'IT', 'JP', 'KR', 'LT', 'LU', 'LV', 'LY', 'MT', 'MY', 'MZ', 'NL', 'PO', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TR', 'UA', 'US' ]`.
 **isPort(str)**                         | check if the string is a valid port number.
 **isPostalCode(str, locale)**           | check if the string is a postal code,<br/><br/>(locale is one of `[ 'AD', 'AT', 'AU', 'AZ', 'BE', 'BG', 'BR', 'BY', 'CA', 'CH', 'CN', 'CZ', 'DE', 'DK', 'DO', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HT', 'HU', 'ID', 'IE' 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'KR', 'LI', 'LT', 'LU', 'LV', 'MT', 'MX', 'MY', 'NL', 'NO', 'NP', 'NZ', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SG', 'SI', 'TH', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM' ]` OR 'any'. If 'any' is used, function will check if any of the locals match. Locale list is `validator.isPostalCodeLocales`.).
 **isRFC3339(str)**                      | check if the string is a valid [RFC 3339](https://tools.ietf.org/html/rfc3339) date.
diff --git a/build-browser.js b/build-browser.js
index 6101d65..c863bd3 100644
--- a/build-browser.js
+++ b/build-browser.js
@@ -1,34 +1,32 @@
 /* eslint import/no-extraneous-dependencies: 0 */
-import fs from 'fs';
-import { rollup } from 'rollup';
-import babel from 'rollup-plugin-babel';
-import babelPresetEnv from '@babel/preset-env';
-import pkg from './package.json';
+import fs from "fs";
+import { rollup } from "rollup";
+import babel from "rollup-plugin-babel";
+import babelPresetEnv from "@babel/preset-env";
+import pkg from "./package.json";
 
 rollup({
-  entry: 'src/index.js',
+  entry: "src/index.js",
   plugins: [
     babel({
       presets: [[babelPresetEnv, { modules: false }]],
       babelrc: false,
     }),
   ],
-}).then(bundle => (
-  bundle.write({
-    dest: 'validator.js',
-    format: 'umd',
-    moduleName: pkg.name,
-    banner: (
-      `/*!\n${
-        String(fs.readFileSync('./LICENSE'))
-          .trim()
-          .split('\n')
-          .map(l => ` * ${l}`)
-          .join('\n')
-      }\n */`
-    ),
-  })
-)).catch((e) => {
-  process.stderr.write(`${e.message}\n`);
-  process.exit(1);
-});
+})
+  .then((bundle) =>
+    bundle.write({
+      dest: "validator.js",
+      format: "umd",
+      moduleName: pkg.name,
+      banner: `/*!\n${String(fs.readFileSync("./LICENSE"))
+        .trim()
+        .split("\n")
+        .map((l) => ` * ${l}`)
+        .join("\n")}\n */`,
+    })
+  )
+  .catch((e) => {
+    process.stderr.write(`${e.message}\n`);
+    process.exit(1);
+  });
diff --git a/package.json b/package.json
index 72573e3..b02fd04 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
     "mocha": "^6.2.3",
     "nyc": "^14.1.0",
     "rimraf": "^3.0.0",
-    "rollup": "^0.43.0",
+    "rollup": "^0.47.0",
     "rollup-plugin-babel": "^4.0.1",
     "uglify-js": "^3.0.19"
   },
@@ -65,11 +65,10 @@
     "build:node": "babel src -d .",
     "build": "npm run build:browser && npm run build:node && npm run build:es",
     "pretest": "npm run build && npm run lint",
-    "test": "nyc mocha --require @babel/register --reporter dot",
-    "test:ci": "nyc report --reporter=text-lcov"
+    "test": "nyc --reporter=cobertura --reporter=text-summary mocha --require @babel/register --reporter dot"
   },
   "engines": {
     "node": ">= 0.10"
   },
   "license": "MIT"
-}
+}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 6759474..59b813c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -48,7 +48,7 @@ import isHSL from './lib/isHSL';
 
 import isISRC from './lib/isISRC';
 
-import isIBAN from './lib/isIBAN';
+import isIBAN, { locales as ibanLocales } from './lib/isIBAN';
 import isBIC from './lib/isBIC';
 
 import isMD5 from './lib/isMD5';
@@ -222,6 +222,7 @@ const validator = {
   isDate,
   isLicensePlate,
   isVAT,
+  ibanLocales,
 };
 
 export default validator;
diff --git a/src/lib/isBoolean.js b/src/lib/isBoolean.js
index e7cb27d..9fddc2b 100644
--- a/src/lib/isBoolean.js
+++ b/src/lib/isBoolean.js
@@ -1,6 +1,15 @@
 import assertString from './util/assertString';
 
-export default function isBoolean(str) {
+const defaultOptions = { loose: false };
+const strictBooleans = ['true', 'false', '1', '0'];
+const looseBooleans = [...strictBooleans, 'yes', 'no'];
+
+export default function isBoolean(str, options = defaultOptions) {
   assertString(str);
-  return (['true', 'false', '1', '0'].indexOf(str) >= 0);
+
+  if (options.loose) {
+    return looseBooleans.includes(str.toLowerCase());
+  }
+
+  return strictBooleans.includes(str);
 }
diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js
index 19853aa..535a957 100644
--- a/src/lib/isIBAN.js
+++ b/src/lib/isIBAN.js
@@ -135,3 +135,5 @@ export default function isIBAN(str) {
 
   return hasValidIbanFormat(str) && hasValidIbanChecksum(str);
 }
+
+export const locales = Object.keys(ibanRegexThroughCountryCode);
diff --git a/src/lib/isIdentityCard.js b/src/lib/isIdentityCard.js
index 872ff19..6461634 100644
--- a/src/lib/isIdentityCard.js
+++ b/src/lib/isIdentityCard.js
@@ -117,6 +117,16 @@ const validators = {
     if (k1 !== f[9] || k2 !== f[10]) return false;
     return true;
   },
+  TH: (str) => {
+    if (!str.match(/^[1-8]\d{12}$/)) return false;
+
+    // validate check digit
+    let sum = 0;
+    for (let i = 0; i < 12; i++) {
+      sum += parseInt(str[i], 10) * (13 - i);
+    }
+    return str[12] === ((11 - (sum % 11)) % 10).toString();
+  },
   'he-IL': (str) => {
     const DNI = /^\d{9}$/;
 
diff --git a/src/lib/isLicensePlate.js b/src/lib/isLicensePlate.js
index a28d66d..d81995b 100644
--- a/src/lib/isLicensePlate.js
+++ b/src/lib/isLicensePlate.js
@@ -1,6 +1,8 @@
 import assertString from './util/assertString';
 
 const validators = {
+  'cs-CZ': str =>
+    /^(([ABCDEFHKIJKLMNPRSTUVXYZ]|[0-9])-?){5,8}$/.test(str),
   'de-DE': str =>
     /^((AW|UL|AK|GA|AÖ|LF|AZ|AM|AS|ZE|AN|AB|A|KG|KH|BA|EW|BZ|HY|KM|BT|HP|B|BC|BI|BO|FN|TT|ÜB|BN|AH|BS|FR|HB|ZZ|BB|BK|BÖ|OC|OK|CW|CE|C|CO|LH|CB|KW|LC|LN|DA|DI|DE|DH|SY|NÖ|DO|DD|DU|DN|D|EI|EA|EE|FI|EM|EL|EN|PF|ED|EF|ER|AU|ZP|E|ES|NT|EU|FL|FO|FT|FF|F|FS|FD|FÜ|GE|G|GI|GF|GS|ZR|GG|GP|GR|NY|ZI|GÖ|GZ|GT|HA|HH|HM|HU|WL|HZ|WR|RN|HK|HD|HN|HS|GK|HE|HF|RZ|HI|HG|HO|HX|IK|IL|IN|J|JL|KL|KA|KS|KF|KE|KI|KT|KO|KN|KR|KC|KU|K|LD|LL|LA|L|OP|LM|LI|LB|LU|LÖ|HL|LG|MD|GN|MZ|MA|ML|MR|MY|AT|DM|MC|NZ|RM|RG|MM|ME|MB|MI|FG|DL|HC|MW|RL|MK|MG|MÜ|WS|MH|M|MS|NU|NB|ND|NM|NK|NW|NR|NI|NF|DZ|EB|OZ|TG|TO|N|OA|GM|OB|CA|EH|FW|OF|OL|OE|OG|BH|LR|OS|AA|GD|OH|KY|NP|WK|PB|PA|PE|PI|PS|P|PM|PR|RA|RV|RE|R|H|SB|WN|RS|RD|RT|BM|NE|GV|RP|SU|GL|RO|GÜ|RH|EG|RW|PN|SK|MQ|RU|SZ|RI|SL|SM|SC|HR|FZ|VS|SW|SN|CR|SE|SI|SO|LP|SG|NH|SP|IZ|ST|BF|TE|HV|OD|SR|S|AC|DW|ZW|TF|TS|TR|TÜ|UM|PZ|TP|UE|UN|UH|MN|KK|VB|V|AE|PL|RC|VG|GW|PW|VR|VK|KB|WA|WT|BE|WM|WE|AP|MO|WW|FB|WZ|WI|WB|JE|WF|WO|W|WÜ|BL|Z|GC)[- ]?[A-Z]{1,2}[- ]?\d{1,4}|(AIC|FDB|ABG|SLN|SAW|KLZ|BUL|ESB|NAB|SUL|WST|ABI|AZE|BTF|KÖT|DKB|FEU|ROT|ALZ|SMÜ|WER|AUR|NOR|DÜW|BRK|HAB|TÖL|WOR|BAD|BAR|BER|BIW|EBS|KEM|MÜB|PEG|BGL|BGD|REI|WIL|BKS|BIR|WAT|BOR|BOH|BOT|BRB|BLK|HHM|NEB|NMB|WSF|LEO|HDL|WMS|WZL|BÜS|CHA|KÖZ|ROD|WÜM|CLP|NEC|COC|ZEL|COE|CUX|DAH|LDS|DEG|DEL|RSL|DLG|DGF|LAN|HEI|MED|DON|KIB|ROK|JÜL|MON|SLE|EBE|EIC|HIG|WBS|BIT|PRÜ|LIB|EMD|WIT|ERH|HÖS|ERZ|ANA|ASZ|MAB|MEK|STL|SZB|FDS|HCH|HOR|WOL|FRG|GRA|WOS|FRI|FFB|GAP|GER|BRL|CLZ|GTH|NOH|HGW|GRZ|LÖB|NOL|WSW|DUD|HMÜ|OHA|KRU|HAL|HAM|HBS|QLB|HVL|NAU|HAS|EBN|GEO|HOH|HDH|ERK|HER|WAN|HEF|ROF|HBN|ALF|HSK|USI|NAI|REH|SAN|KÜN|ÖHR|HOL|WAR|ARN|BRG|GNT|HOG|WOH|KEH|MAI|PAR|RID|ROL|KLE|GEL|KUS|KYF|ART|SDH|LDK|DIL|MAL|VIB|LER|BNA|GHA|GRM|MTL|WUR|LEV|LIF|STE|WEL|LIP|VAI|LUP|HGN|LBZ|LWL|PCH|STB|DAN|MKK|SLÜ|MSP|TBB|MGH|MTK|BIN|MSH|EIL|HET|SGH|BID|MYK|MSE|MST|MÜR|WRN|MEI|GRH|RIE|MZG|MIL|OBB|BED|FLÖ|MOL|FRW|SEE|SRB|AIB|MOS|BCH|ILL|SOB|NMS|NEA|SEF|UFF|NEW|VOH|NDH|TDO|NWM|GDB|GVM|WIS|NOM|EIN|GAN|LAU|HEB|OHV|OSL|SFB|ERB|LOS|BSK|KEL|BSB|MEL|WTL|OAL|FÜS|MOD|OHZ|OPR|BÜR|PAF|PLÖ|CAS|GLA|REG|VIT|ECK|SIM|GOA|EMS|DIZ|GOH|RÜD|SWA|NES|KÖN|MET|LRO|BÜZ|DBR|ROS|TET|HRO|ROW|BRV|HIP|PAN|GRI|SHK|EIS|SRO|SOK|LBS|SCZ|MER|QFT|SLF|SLS|HOM|SLK|ASL|BBG|SBK|SFT|SHG|MGN|MEG|ZIG|SAD|NEN|OVI|SHA|BLB|SIG|SON|SPN|FOR|GUB|SPB|IGB|WND|STD|STA|SDL|OBG|HST|BOG|SHL|PIR|FTL|SEB|SÖM|SÜW|TIR|SAB|TUT|ANG|SDT|LÜN|LSZ|MHL|VEC|VER|VIE|OVL|ANK|OVP|SBG|UEM|UER|WLG|GMN|NVP|RDG|RÜG|DAU|FKB|WAF|WAK|SLZ|WEN|SOG|APD|WUG|GUN|ESW|WIZ|WES|DIN|BRA|BÜD|WHV|HWI|GHC|WTM|WOB|WUN|MAK|SEL|OCH|HOT|WDA)[- ]?(([A-Z][- ]?\d{1,4})|([A-Z]{2}[- ]?\d{1,3})))[- ]?(E|H)?$/.test(str),
   'de-LI': str => /^FL[- ]?\d{1,5}[UZ]?$/.test(str),
diff --git a/src/lib/isMobilePhone.js b/src/lib/isMobilePhone.js
index 95b5685..48e167d 100644
--- a/src/lib/isMobilePhone.js
+++ b/src/lib/isMobilePhone.js
@@ -25,7 +25,7 @@ const phones = {
   'ca-AD': /^(\+376)?[346]\d{5}$/,
   'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
   'da-DK': /^(\+?45)?\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}$/,
-  'de-DE': /^(\+49)?0?[1|3]([0|5][0-45-9]\d|6([23]|0\d?)|7([0-57-9]|6\d))\d{7}$/,
+  'de-DE': /^((\+49|0)[1|3])([0|5][0-45-9]\d|6([23]|0\d?)|7([0-57-9]|6\d))\d{7,9}$/,
   'de-AT': /^(\+43|0)\d{1,4}\d{3,12}$/,
   'de-CH': /^(\+41|0)([1-9])\d{1,9}$/,
   'de-LU': /^(\+352)?((6\d1)\d{6})$/,
@@ -113,8 +113,8 @@ const phones = {
   'tr-TR': /^(\+?90|0)?5\d{9}$/,
   'uk-UA': /^(\+?38|8)?0\d{9}$/,
   'uz-UZ': /^(\+?998)?(6[125-79]|7[1-69]|88|9\d)\d{7}$/,
-  'vi-VN': /^(\+?84|0)((3([2-9]))|(5([2689]))|(7([0|6-9]))|(8([1-9]))|(9([0-9])))([0-9]{7})$/,
-  'zh-CN': /^((\+|00)86)?1([3456789][0-9]|4[579]|6[67]|7[01235678]|9[012356789])[0-9]{8}$/,
+  'vi-VN': /^((\+?84)|0)((3([2-9]))|(5([25689]))|(7([0|6-9]))|(8([1-9]))|(9([0-9])))([0-9]{7})$/,
+  'zh-CN': /^((\+|00)86)?1([3456789][0-9]|4[579]|6[2567]|7[01235678]|9[012356789])[0-9]{8}$/,
   'zh-TW': /^(\+?886\-?|0)?9\d{8}$/,
 };
 /* eslint-enable max-len */
diff --git a/src/lib/isPassportNumber.js b/src/lib/isPassportNumber.js
index 51a8e70..b283a27 100644
--- a/src/lib/isPassportNumber.js
+++ b/src/lib/isPassportNumber.js
@@ -33,6 +33,7 @@ const passportRegexByCountryCode = {
   HU: /^[A-Z]{2}(\d{6}|\d{7})$/, // HUNGARY
   IE: /^[A-Z0-9]{2}\d{7}$/, // IRELAND
   IN: /^[A-Z]{1}-?\d{7}$/, // INDIA
+  ID: /^[A-C]\d{7}$/, // INDONESIA
   IR: /^[A-Z]\d{8}$/, // IRAN
   IS: /^(A)\d{7}$/, // ICELAND
   IT: /^[A-Z0-9]{2}\d{7}$/, // ITALY
diff --git a/src/lib/isTaxID.js b/src/lib/isTaxID.js
index f1ebae6..ee66ca3 100644
--- a/src/lib/isTaxID.js
+++ b/src/lib/isTaxID.js
@@ -860,14 +860,10 @@ function plPlCheck(tin) {
 */
 
 function ptBrCheck(tin) {
-  tin = tin.replace(/[^\d]+/g, '');
-  if (tin === '') return false;
-
   if (tin.length === 11) {
     let sum;
-    let ramainder;
+    let remainder;
     sum = 0;
-    tin = tin.replace(/[^\d]+/g, '');
 
     if ( // Reject known invalid CPFs
       tin === '11111111111' ||
@@ -883,21 +879,19 @@ function ptBrCheck(tin) {
     ) return false;
 
     for (let i = 1; i <= 9; i++) sum += parseInt(tin.substring(i - 1, i), 10) * (11 - i);
-    ramainder = (sum * 10) % 11;
-    if ((ramainder === 10) || (ramainder === 11)) ramainder = 0;
-    if (ramainder !== parseInt(tin.substring(9, 10), 10)) return false;
+    remainder = (sum * 10) % 11;
+    if (remainder === 10) remainder = 0;
+    if (remainder !== parseInt(tin.substring(9, 10), 10)) return false;
     sum = 0;
 
     for (let i = 1; i <= 10; i++) sum += parseInt(tin.substring(i - 1, i), 10) * (12 - i);
-    ramainder = (sum * 10) % 11;
-    if ((ramainder === 10) || (ramainder === 11)) ramainder = 0;
-    if (ramainder !== parseInt(tin.substring(10, 11), 10)) return false;
+    remainder = (sum * 10) % 11;
+    if (remainder === 10) remainder = 0;
+    if (remainder !== parseInt(tin.substring(10, 11), 10)) return false;
 
     return true;
   }
 
-  if (tin.length !== 14) { return false; }
-
   if ( // Reject know invalid CNPJs
     tin === '00000000000000' ||
     tin === '11111111111111' ||
@@ -1126,7 +1120,7 @@ const taxIdFormat = {
   'mt-MT': /^\d{3,7}[APMGLHBZ]$|^([1-8])\1\d{7}$/i,
   'nl-NL': /^\d{9}$/,
   'pl-PL': /^\d{10,11}$/,
-  'pt-BR': /^\d{11,14}$/,
+  'pt-BR': /(?:^\d{11}$)|(?:^\d{14}$)/,
   'pt-PT': /^\d{9}$/,
   'ro-RO': /^\d{13}$/,
   'sk-SK': /^\d{6}\/{0,1}\d{3,4}$/,


Test patch:
diff --git a/test/exports.js b/test/exports.js
index 32daa99..0bff532 100644
--- a/test/exports.js
+++ b/test/exports.js
@@ -5,6 +5,7 @@ import { locales as isAlphaLocales } from '../src/lib/isAlpha';
 import { locales as isAlphanumericLocales } from '../src/lib/isAlphanumeric';
 import { locales as isMobilePhoneLocales } from '../src/lib/isMobilePhone';
 import { locales as isFloatLocales } from '../src/lib/isFloat';
+import { locales as ibanCountryCodes } from '../src/lib/isIBAN';
 
 describe('Exports', () => {
   it('should export validators', () => {
@@ -50,4 +51,9 @@ describe('Exports', () => {
     assert.ok(isFloatLocales instanceof Array);
     assert.ok(validator.isFloatLocales instanceof Array);
   });
+
+  it('should export a list of country codes that implement IBAN', () => {
+    assert.ok(ibanCountryCodes instanceof Array);
+    assert.ok(validator.ibanLocales instanceof Array);
+  });
 });
diff --git a/test/validators.js b/test/validators.js
index 56e581d..6bb6072 100644
--- a/test/validators.js
+++ b/test/validators.js
@@ -2345,6 +2345,22 @@ describe('Validators', () => {
       ],
     });
 
+
+    test({
+      validator: 'isPassportNumber',
+      args: ['ID'],
+      valid: [
+        'C1253473',
+        'B5948378',
+        'A4859472',
+      ],
+      invalid: [
+        'D39481728',
+        'A-3847362',
+        '324132132',
+      ],
+    });
+
     test({
       validator: 'isPassportNumber',
       args: ['AR'],
@@ -4674,6 +4690,24 @@ describe('Validators', () => {
           '92031470790',
         ],
       },
+      {
+        locale: 'TH',
+        valid: [
+          '1101230000001',
+          '1101230000060',
+        ],
+        invalid: [
+          'abc',
+          '1101230',
+          '11012300000011',
+          'aaaaaaaaaaaaa',
+          '110123abcd001',
+          '1101230000007',
+          '0101123450000',
+          '0101123450004',
+          '9101123450008',
+        ],
+      },
       {
         locale: 'he-IL',
         valid: [
@@ -5812,21 +5846,20 @@ describe('Validators', () => {
       {
         locale: 'de-DE',
         valid: [
-          '+49015123456789',
           '+4915123456789',
           '+4930405044550',
           '015123456789',
-          '15123456789',
-          '15623456789',
-          '15623456789',
-          '1601234567',
-          '16012345678',
-          '1621234567',
-          '1631234567',
-          '1701234567',
-          '17612345678',
-          '15345678910',
-          '15412345678',
+          '015123456789',
+          '015623456789',
+          '015623456789',
+          '01601234567',
+          '016012345678',
+          '01621234567',
+          '01631234567',
+          '01701234567',
+          '017612345678',
+          '015345678910',
+          '015412345678',
         ],
         invalid: [
           '34412345678',
@@ -5836,6 +5869,7 @@ describe('Validators', () => {
           '16412345678',
           '17012345678',
           '+4912345678910',
+          '+49015123456789',
         ],
       },
       {
@@ -5971,6 +6005,9 @@ describe('Validators', () => {
           '16565600001',
           '+8617269427292',
           '008617269427292',
+          '16238234822',
+          '008616238234822',
+          '+8616238234822',
         ],
         invalid: [
           '12345',
@@ -6729,6 +6766,8 @@ describe('Validators', () => {
           '0892405867',
           '+84888696413',
           '0878123456',
+          '84781234567',
+          '0553803765',
         ],
         invalid: [
           '12345',
@@ -6740,6 +6779,8 @@ describe('Validators', () => {
           '+841698765432',
           '841626543219',
           '0533803765',
+          '08712345678',
+          '+0321234567',
         ],
       },
       {
@@ -8854,6 +8895,37 @@ describe('Validators', () => {
     });
   });
 
+  it('should validate booleans with option loose set to true', () => {
+    test({
+      validator: 'isBoolean',
+      args: [
+        { loose: true },
+      ],
+      valid: [
+        'true',
+        'True',
+        'TRUE',
+        'false',
+        'False',
+        'FALSE',
+        '0',
+        '1',
+        'yes',
+        'Yes',
+        'YES',
+        'no',
+        'No',
+        'NO',
+      ],
+      invalid: [
+        '1.0',
+        '0.0',
+        'true ',
+        ' false',
+      ],
+    });
+  });
+
   const validISO8601 = [
     '2009-12T12:34',
     '2009',
@@ -10260,12 +10332,19 @@ describe('Validators', () => {
         '05423994000172',
         '11867044000130'],
       invalid: [
+        'ABCDEFGH',
         '170.691.440-72',
-        '01494282042',
+        '11494282142',
+        '74405265037',
         '11111111111',
+        '48469799384',
         '94.592.973/0001-82',
         '28592361000192',
-        '11111111111111'],
+        '11111111111111',
+        '111111111111112',
+        '61938188550993',
+        '82168365502729',
+      ],
     });
     test({
       validator: 'isTaxID',
@@ -10677,6 +10756,31 @@ describe('Validators', () => {
         'AAA 00 AAA',
       ],
     });
+    test({
+      validator: 'isLicensePlate',
+      args: ['cs-CZ'],
+      valid: [
+        'ALA4011',
+        '4A23000',
+        'DICTAT0R',
+        'VETERAN',
+        'AZKVIZ8',
+        '2A45876',
+        'DIC-TAT0R',
+      ],
+      invalid: [
+        '',
+        'invalidlicenseplate',
+        'LN5758898',
+        'X-|$|-X',
+        'AE0F-OP4',
+        'GO0MER',
+        '2AAAAAAAA',
+        'FS AB 1234 E',
+        'GB999 9999 00',
+      ],
+    });
+
     test({
       validator: 'isLicensePlate',
       args: ['pt-BR'],