Skip to content

Internationalization (i18n) ​

QuickModel's @QRule validation produces error messages as strings. When your application supports multiple languages, you can plug in any i18n library to translate those strings automatically — without changing your model definitions.

How it works ​

Configure a resolver function in QConfig. Every time a rule fails, QuickModel passes the raw error message (or key) through the resolver before returning it to the caller:

typescript
import { QConfig } from 'quickmodel';

QConfig.configure({
	i18n: {
		resolver: (key) => myI18nLibrary.translate(key),
	},
});

The resolver is called only for failing rules — never for passing ones.

Basic example ​

typescript
QConfig.configure({
	i18n: {
		resolver: (key) => t(key), // any translation function
	},
});

@Quick()
class UserModel extends QModel<IUser> {
	@QRule(
		(val: string) => val.length >= 3,
		'validation.name.minLength' // i18n key
	)
	declare name: string;
}

const result = new UserModel({ name: 'Al' }).$qCheckRules();
// → { valid: false, errors: [{ field: 'name', message: 'Name must be at least 3 characters' }] }
//   The resolver translated 'validation.name.minLength' for the active locale

Integration with common i18n libraries ​

vue-i18n ​

typescript
import { createI18n } from 'vue-i18n';
import { QConfig } from 'quickmodel';

const i18n = createI18n({ ... });

QConfig.configure({
  i18n: {
    resolver: (key) => String(i18n.global.t(key)),
  },
});

react-i18next ​

typescript
import i18next from 'i18next';
import { QConfig } from 'quickmodel';

QConfig.configure({
	i18n: {
		resolver: (key) => i18next.t(key),
	},
});

Angular TranslateService ​

typescript
import { TranslateService } from '@ngx-translate/core';

export function setupQuickModelI18n(translate: TranslateService): void {
	QConfig.configure({
		i18n: {
			resolver: (key) => translate.instant(key),
		},
	});
}

No library — static map ​

typescript
const messages: Record<string, string> = {
	'validation.name.minLength': 'Name must be at least 3 characters',
	'validation.email.format': 'Invalid email format',
};

QConfig.configure({
	i18n: {
		resolver: (key) => messages[key] ?? key, // fallback to key if missing
	},
});

Strategy 1 — i18n keys in @QRule ​

Store translation keys instead of inline strings. The resolver translates them at runtime:

typescript
@Quick()
class ProductModel extends QModel<IProduct> {
	@QRule((v: string) => v.length > 0, 'validation.title.required')
	@QRule((v: string) => v.length <= 200, 'validation.title.maxLength')
	declare title: string;

	@QRule((v: number) => v > 0, 'validation.price.positive')
	declare price: number;
}

Advantages: Model is completely locale-agnostic. Changing translations doesn't require touching the model.

Strategy 2 — Lazy () => string resolver in @QRule ​

Pass a function instead of a string. It is evaluated at checkRules() call-time, picking up the current locale:

typescript
import { t } from './i18n';

@Quick()
class UserModel extends QModel<IUser> {
	@QRule(
		(val: string) => val.length >= 3,
		() => t('validation.name.minLength') // called at rule-check time
	)
	declare name: string;
}

Advantages: No global QConfig.i18n resolver needed. Works when different parts of the app have different locale contexts.

Strategy 3 — Combine both ​

Use lazy resolvers in @QRule for field-specific messages, and a global QConfig.i18n.resolver as a universal fallback for any remaining keys:

typescript
QConfig.configure({
	i18n: {
		resolver: (key) => t(key), // global fallback for all models
	},
});

@Quick()
class UserModel extends QModel<IUser> {
	@QRule(
		(val: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),
		() => t('validation.email.format') // lazy, locale-aware
	)
	declare email: string;

	@QRule((v: string) => v.length >= 8, 'validation.password.minLength')
	declare password: string; // uses global resolver via QConfig
}

Angular template pipe pattern ​

In Angular, you can store plain keys and apply | translate in templates — no global resolver needed:

typescript
@Quick()
class FormModel extends QModel<IFormData> {
	@QRule((v: string) => v !== '', 'FORM.NAME_REQUIRED')
	declare name: string;
}
html
<!-- Template: translate the key returned in e.message -->
<span *ngFor="let e of errors">{{ e.message | translate }}</span>

Async rules and i18n ​

The resolver also applies to errors from checkRulesAsync():

typescript
const result = await model.$qCheckRulesAsync();
// → errors[].message is also passed through the resolver

API reference ​

SymbolDescription
QConfig.configure({ i18n: { resolver } })Registers a global message resolver
resolver(key: string): stringReceives the raw message or key, returns the translated string
@QRule(pred, 'key')Uses a string key as the error message
@QRule(pred, () => string)Lazy function evaluated at rule-check time

See also ​