i18n Customization
Overview
TeaQL internationalization is about natural language support for domain systems. It is not only UI translation. It can affect validation messages, field labels, entity names, user-facing error text, exported data, and locale-aware query behavior.
CustomUserContext is the recommended place to resolve the active language and expose project-specific translation behavior.
What Can Be Localized
TeaQL projects commonly customize:
| Area | Examples |
|---|---|
| Entity display names | Customer, Order, Invoice |
| Field labels | customerName, orderStatus, createdAt |
| Validation messages | Required field, invalid status, permission denied |
| Business errors | Duplicate order, stock shortage, workflow conflict |
| Dynamic properties | Frontend-facing computed fields |
| Sorting | Locale-aware name ordering and collation |
| Exported content | CSV, Excel, PDF labels and values |
Recommended Design
Language resolution should happen once in CustomUserContext.
Typical language sources:
- User profile preference
- Request header such as
Accept-Language - Tenant default language
- Explicit query parameter
- System fallback language
Recommended priority:
request override
-> user preference
-> tenant default
-> application default
Example:
public class CustomUserContext extends UserContext {
public Locale currentLocale() {
String requested = getRequestHeader("Accept-Language");
if (requested != null && !requested.isBlank()) {
return Locale.forLanguageTag(requested);
}
return currentUser().preferredLocale();
}
}
Adapt request and user-profile access to your project.
Translation Service
Keep translations behind a project service:
public interface DomainTranslator {
String translate(String key, Locale locale);
String translate(String key, Locale locale, Map<String, Object> args);
}
Expose it from CustomUserContext:
public class CustomUserContext extends UserContext {
public DomainTranslator translator() {
return getBean(DomainTranslator.class);
}
public String t(String key) {
return translator().translate(key, currentLocale());
}
public String t(String key, Map<String, Object> args) {
return translator().translate(key, currentLocale(), args);
}
}
Usage:
throw new BusinessException(userContext.t("order.status.cannot_cancel"));
Validation Message Translation
Validation is one of the most important i18n integration points.
Instead of hard-coding text:
throw new BusinessException("Customer name is required");
Prefer semantic keys:
throw new BusinessException(userContext.t("customer.name.required"));
Example translations:
customer.name.required=Customer name is required.
order.status.cannot_cancel=The order cannot be cancelled in its current status.
customer.name.required=客户名称不能为空。
order.status.cannot_cancel= 当前状态下订单不能取消。
Entity and Field Labels
Domain labels can be stored with stable keys:
entity.customer=Customer
entity.order=Order
field.customer.name=Customer Name
field.order.status=Order Status
These labels are useful for:
- Form rendering
- Validation errors
- Export headers
- Audit logs
- Admin tools
Example:
public String fieldLabel(String entityName, String fieldName) {
return t("field." + entityName + "." + fieldName);
}
Locale-Aware Sorting
Some generated TeaQL query methods may expose locale-aware ordering, for example:
Q.users().orderByNameAscendingUsingGBK()
When a project supports multiple natural languages, choose sorting behavior from the context:
public UserRequest orderUsersByDisplayName(UserRequest request) {
if (Locale.SIMPLIFIED_CHINESE.equals(currentLocale())) {
return request.orderByNameAscendingUsingGBK();
}
return request.orderByNameAscending();
}
Best Practices
- Store translation keys, not hard-coded messages, in business logic.
- Resolve the active locale in
CustomUserContext. - Keep a fallback language for missing translations.
- Include entity and field labels in the translation set.
- Test validation messages in every supported language.
- Keep audit records stable; translate display text at read time when possible.
Testing Checklist
- Default language works without request headers.
- User preference overrides tenant default.
- Request override works when allowed.
- Missing translation keys fall back safely.
- Validation errors are translated.
- Locale-aware sorting returns expected order.
- Exports use localized labels.
Summary
TeaQL i18n customization should be driven by CustomUserContext. The context knows the user, tenant, request, and locale, so it is the natural place to connect generated domain behavior with project-specific natural language requirements.