Multilingual Configuration Guide

📚Multi-language document navigation

Choose the appropriate document based on your needs:

  • 🚀 Get started quickly: i18n.md (current document) - Learn basic usage in 5 minutes
  • 📖 Full User Guide: i18n-user-guide.md - Detailed user guide and best practices
  • 🎯 Add new language: add-custom-locale.md - Tutorial on adding a custom language pack
  • 🔄 Dynamic language switching: dynamic-locale.md - Dynamic language switching in API development
  • 🌐 Front-end integration: frontend-i18n-guide.md - Front-end and back-end separation project integration guide

📖 Overview

schema-dsl supports full multi-language functionality, allowing you to customize translations of field labels and error messages.

Node.js Requirements: >=18.0.0

Default supported language file formats for directory loading (Node >=18):

  • .js (CommonJS language pack)
  • .cjs
  • .json
  • .jsonc
  • .json5

Recommendation: If your application is an type: module / ESM project, give priority to using .cjs, .json, .jsonc, .json5; .js is more suitable for CommonJS language pack files.

v2 current capabilities:

  • ✅ Support parameterized language switching (no need to modify global state)
  • ✅ Supports custom error messages (three formats)
  • ✅ TypeScript type definitions are complete
  • ✅ Directory recursive loading .js/.cjs/.json/.jsonc/.json5

🚀 Quick start

import { s, validate } from 'schema-dsl/pure';

const schema = s({ username: 'string:3-32!' });

// Use Chinese error message
const result = validate(schema, { username: 'ab' }, { locale: 'zh-CN' });
// message: "username cannot be less than 3 characters long"

// Use English error message
const result2 = validate(schema, { username: 'ab' }, { locale: 'en-US' });
// message: "username length must be at least 3"

Method 2: Use global configuration (backwards compatible)

import { Locale } from 'schema-dsl/pure';

//Set default language
Locale.setLocale('zh-CN');

// Subsequent validation will use Chinese error messages
const result = validate(schema, data);

📝 Built-in language support

schema-dsl has the following built-in language packages:

language codeLanguage nameSupport status
zh-CNSimplified Chinese✅ Full support
en-USEnglish (United States)✅ Full support
ja-JPJapanese✅ Full support
es-ESspanish✅ Full support
fr-FRFrench✅ Full support

🎯 Advanced usage

Basic configuration

import { s, validate } from 'schema-dsl/pure';
import path from 'path';
import zhCN from './i18n/dsl/zh-CN.cjs';
import enUS from './i18n/dsl/en-US.cjs';

// ✅ Method 1: Load from directory (recommended)
s.config({
  i18n: path.join(__dirname, 'i18n/dsl') // Node >=18: supports .js/.cjs/.json/.jsonc/.json5
});

// ✅ Method 2: Directly pass in the object
s.config({
  i18n: {
    'zh-CN': zhCN,
    'en-US': enUS
  }
});

Directory loading keeps .js / .cjs enabled by default for backwards compatibility. Those files are executed by Node, so use them only for trusted locale directories, or set codeLocaleFiles: 'deny' to load only .json, .jsonc, and .json5.

Directory structure

project/
├── i18n/
│ └── dsl/ # schema-dsl language pack directory
│ ├── zh-CN.cjs # CommonJS / ESM projects are stable
│ ├── en-US.jsonc # with comments/end comma
│ └── ja-JP.json5 # JSON5 style (optional)

📝 Language pack format

Full example (i18n/dsl/zh-CN.cjs)

module.exports = {
  // ========== Field labels ==========
  'field.username': 'username',
  'field.email': 'Email address',
  'field.password': 'password',
  'field.age': 'Age',

  // ========== Override default error message ==========
  'required': '{{#label}} is required',
  'string.minLength': '{{#label}} cannot be less than {{#limit}} characters',
  'string.maxLength': '{{#label}} cannot exceed {{#limit}} characters',
  'string.enum': '{{#label}} must be one of the following values: {{#valids}}',
  'number.base': '{{#label}} must be a numeric type',
  'number.min': '{{#label}} cannot be less than {{#limit}}',
  'number.max': '{{#label}} cannot be greater than {{#limit}}',
  'boolean.base': '{{#label}} must be of Boolean type',
  'enum': '{{#label}} must be one of the following values: {{#allowed}}',

  // ========== Custom error message ==========
  'custom.invalidEmail': 'The email format is incorrect, please re-enter',
  'custom.emailTaken': 'This email address has been registered',
  'custom.passwordWeak': 'The password is not strong enough'
};

🎯 How to use

1. Field label translation

const schema = s({
  username: s('string:3-32!').label('field.username'),
  email: s('email!').label('field.email')
});

const result = validate(schema, { username: 'ab' });
// Error message: "Username cannot be less than 3 characters long"

2. Override the default error message

// language pack
module.exports = {
  'required': '{{#label}} must be filled in'
};

// Schema
const schema = s({ username: 'string!' });

validate(schema, {});
// Error message: "username must be filled in"

3. Customize error messages

const schema = s({
  status: s('active|inactive').messages({
    'enum': 'Status must be active or inactive'
  })
});

4. Dynamic language switching

s.config({
  i18n: {
    'zh-CN': { 'required': '{{#label}} is required' },
    'en-US': { 'required': '{{#label}} is required' }
  }
});

//Default language (en-US)
validate(schema, data);

// switch to English
validate(schema, data, { locale: 'en-US' });

📋 Built-in error message key

Generic error

wrong keyDescriptionChinese message example
requiredRequired fields are missing{{#label}} is required
enumEnumeration value is not in range{{#label}} must be one of the following values: {{#allowed}}
patternIncorrect format{{#label}} is incorrectly formatted

String error

wrong keyDescription
string.minLengthminimum length
string.maxLengthmaximum length
string.patternRegularity does not match
string.enumstring enum

Number error

wrong keyDescription
number.basetype is not a number
number.minless than minimum
number.maxgreater than maximum
number.integernot an integer

Boolean error

wrong keyDescription
boolean.basetype is not a boolean

Array error

wrong keyDescription
array.basetype is not an array
array.minNot enough elements
array.maxToo many elements
array.uniqueContains repeated elements

Format error

wrong keyDescription
format.emailEmail format error
format.urlURL format error
format.uuidUUID format error
format.dateDate format error
format.binaryBase64 encoding error

Pattern error

wrong keyDescription
pattern.phoneMobile phone number format error
pattern.idCardID card format error
pattern.objectIdObjectId format error

For the complete list, please refer to: src/locales/zh-CN.ts


🔧 Advanced usage

Nested field labels

// language pack
module.exports = {
  'field.address.city': 'city',
  'field.address.street': 'street'
};

// Schema
const schema = s({
  address: {
    city: s('string!').label('field.address.city'),
    street: s('string!').label('field.address.street')
  }
});

Error message priority

  1. Field-level custom message - .messages() method
  2. Schema-level tag - .label() method
  3. Language pack custom message - i18n Configuration
  4. Default Error Message - Built-in language pack
// Priority example
s.config({
  i18n: {
    'zh-CN': {
      'required': 'Global required message' // Priority 3
    }
  }
});

const schema = s({
  username: s('string!')
    .label('username') // Priority 2
    .messages({ 'required': 'Required' }) // Priority 1 (highest)
});

Multilingual best practices

1. Directory structure

project/
├── i18n/
│   └── dsl/
│ ├── zh-CN.cjs # Chinese
│ ├── en-US.cjs # English
│ ├── ja-JP.cjs # Japanese
│ └── index.js # Export tool

2. Export tool (i18n/dsl/index.js)

import zhCN from './zh-CN.cjs';
import enUS from './en-US.cjs';
import jaJP from './ja-JP.cjs';

export default {
  'zh-CN': zhCN,
  'en-US': enUS,
  'ja-JP': jaJP
};

// use
import localeMessages from './i18n/dsl/index.js';

s.config({
  i18n: localeMessages
});

3. Message reuse

// i18n/dsl/common.cjs
module.exports = {
  required: '{{#label}} is required',
  minLength: '{{#label}} cannot be less than {{#limit}} characters'
};

// i18n/dsl/zh-CN.cjs
import common from './common.cjs';

module.exports = {
  ...common,
  'field.username': 'username',
  'field.email': 'Email'
};

📊 Full example

User management system

import { s, validate } from 'schema-dsl/pure';
import path from 'path';

// Configure multiple languages
s.config({
  i18n: path.join(__dirname, 'i18n/dsl')
});

//define Schema
const userSchema = s({
  username: s('string:3-32!').label('field.username'),
  email: s('email!').label('field.email'),
  password: s('string:8-32!').label('field.password'),
  age: s('number:18-120').label('field.age'),
  role: s('admin|user|guest!').label('field.role')
});

// Validation (Chinese)
let result = validate(userSchema, {
  username: 'ab',
  email: 'invalid',
  role: 'admin'
});

console.log(result.errors);
// [
// { path: 'username', message: 'The username cannot be less than 3 characters in length' },
// { path: 'email', message: 'The email address must be a valid email address' },
// { path: 'password', message: 'Password is required' }
// ]

// Validation (English)
result = validate(userSchema, {
  username: 'ab'
}, { locale: 'en-US' });

console.log(result.errors[0].message);
// "username must be at least 3 characters"

🐛 FAQ

1. The language pack does not take effect?

Check these common causes first:

  • The configuration key should be i18n, not locales.
  • The directory path should point to the folder that contains your locale files.
  • The file name should be a locale code, such as zh-CN.cjs or zh-CN.jsonc.
  • .js locale files should be CommonJS (module.exports). In ESM projects, prefer .cjs or .json*.

2. Is the error message still in English?

Cause: The current request does not explicitly pass in locale, or the global default language has not yet been switched to the language you expect.

// Method 1: Global settings
import { Locale } from 'schema-dsl/pure';
Locale.setLocale('zh-CN');

//Method 2: Specify during validation
validate(schema, data, { locale: 'zh-CN' });

3. Customized messages are not displayed?

Check Priority:

  • .messages()s( > ).label() > Language Pack > Default
// Error: label will be overwritten by messages
s('string!')
  .label('username')
  .messages({ 'required': 'required' }) // This takes effect


Corresponding sample file

Example entry: i18n.ts Description: Overrides built-in locale switching, field-level message priority, and the minimum working path of custom locales.