@libs-ui/services-translate
v0.2.357-4
Published
> Dịch vụ i18n cho Angular — mở rộng `@ngx-translate/core` với MessageFormat, global interpolation và tự động merge chuỗi dịch nội bộ.
Readme
@libs-ui/services-translate
Dịch vụ i18n cho Angular — mở rộng
@ngx-translate/corevới MessageFormat, global interpolation và tự động merge chuỗi dịch nội bộ.
Giới thiệu
@libs-ui/services-translate cung cấp LibsUiTranslateService — một lớp kế thừa từ TranslateService của @ngx-translate/core, tích hợp sẵn TranslateMessageFormatCompiler để xử lý cú pháp plural/select phức tạp. Service tự động merge các chuỗi dịch nội bộ của @libs-ui vào mọi ngôn ngữ, đồng thời cho phép thiết lập biến nội suy toàn cục áp dụng cho tất cả chuỗi dịch mà không cần truyền thủ công. Ngôn ngữ đang chọn được lưu tự động qua UtilsCache.
Tính năng
- ✅ Kế thừa toàn bộ API của
TranslateService— inject và dùng như bình thường - ✅ Hỗ trợ cú pháp MessageFormat — xử lý
plural,select,ordinal - ✅ Global interpolation — thiết lập biến dùng chung cho toàn bộ chuỗi dịch (tên thương hiệu, hotline, ...)
- ✅ Tự động merge chuỗi dịch nội bộ
@libs-ui(hơn 100 key dùng cho các component UI) - ✅ Load file JSON qua HTTP với
TranslateHttpLoader(đường dẫn tùy chỉnh) - ✅ Inject translation trực tiếp từ object (không cần file
.json) - ✅ Lưu ngôn ngữ hiện tại vào
UtilsCache(localStorage) khi gọiuse() - ✅ Hỗ trợ hai ngôn ngữ:
'vi'(Tiếng Việt) và'en'(English) - ✅ Safe fallback —
instant()vàget()trả về key gốc nếu key rỗng/undefined
Khi nào sử dụng
- Ứng dụng cần hỗ trợ đa ngôn ngữ và chuyển đổi ngôn ngữ tại runtime
- Cần xử lý số ít/số nhiều (plural) hoặc chọn chuỗi theo giá trị (select) với cú pháp MessageFormat
- Cần thiết lập biến toàn cục (tên thương hiệu, hotline, đơn vị tiền tệ) tự động xuất hiện trong mọi chuỗi dịch
- Lib hoặc shared module cần inject chuỗi dịch từ code (object) thay vì phụ thuộc file JSON của từng app
- Muốn các component
@libs-uihiển thị đúng ngôn ngữ mà không cần cấu hình thêm
Cài đặt
npm install @libs-ui/services-translateImport
// Cấu hình providers (bắt buộc)
import { getConfigTranslate } from '@libs-ui/services-translate';
// Thiết lập biến nội suy toàn cục (tùy chọn)
import { setInterpolateParamDefault, getInterpolateParamDefault } from '@libs-ui/services-translate';
// Sử dụng trong component (inject qua @ngx-translate/core như bình thường)
import { TranslateService, TranslateModule } from '@ngx-translate/core';Ví dụ sử dụng
1. Cấu hình providers trong app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { getConfigTranslate } from '@libs-ui/services-translate';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(appRoutes),
// Load từ /i18n/*.json (mặc định)
getConfigTranslate(),
],
};Cấu trúc thư mục file dịch mặc định (đặt trong public/):
public/
└── i18n/
├── vi.json
└── en.json2. Cấu hình đường dẫn tùy chỉnh
import { ApplicationConfig } from '@angular/core';
import { getConfigTranslate } from '@libs-ui/services-translate';
export const appConfig: ApplicationConfig = {
providers: [
// Load từ /assets/translations/*.json
getConfigTranslate('/assets/translations/'),
],
};3. Khởi tạo ngôn ngữ và inject translation từ object
import { Component, inject, OnInit } from '@angular/core';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-root',
standalone: true,
imports: [TranslateModule],
template: `
<span>{{ 'i18n_search' | translate }}</span>
<span>{{ 'i18n_greeting' | translate: { name: 'Admin' } }}</span>
`,
})
export class AppComponent implements OnInit {
private readonly translate = inject(TranslateService);
ngOnInit(): void {
// Inject translation từ object (không cần file .json)
this.translate.setTranslation('vi', {
i18n_greeting: 'Xin chào, {name}!',
i18n_welcome: 'Chào mừng đến với hệ thống',
}, true); // true = merge, không ghi đè key có sẵn
this.translate.setTranslation('en', {
i18n_greeting: 'Hello, {name}!',
i18n_welcome: 'Welcome to the system',
}, true);
// Đặt ngôn ngữ mặc định
this.translate.use('vi');
}
}4. Chuyển đổi ngôn ngữ runtime
import { Component, inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { TYPE_LANGUAGE_SUPPORT } from '@libs-ui/interfaces-types';
@Component({
selector: 'app-language-switcher',
standalone: true,
template: `
<button (click)="handlerSwitchLang('vi')">Tiếng Việt</button>
<button (click)="handlerSwitchLang('en')">English</button>
`,
})
export class LanguageSwitcherComponent {
private readonly translate = inject(TranslateService);
handlerSwitchLang(event: Event, lang: TYPE_LANGUAGE_SUPPORT): void {
event.stopPropagation();
this.translate.use(lang);
// Ngôn ngữ được tự động lưu vào UtilsCache (localStorage)
}
}5. Dùng instant() và get() trong TypeScript
import { Component, inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-notification',
standalone: true,
template: '',
})
export class NotificationComponent {
private readonly translate = inject(TranslateService);
private readonly destroyRef = inject(DestroyRef);
// Đồng bộ (sync) — dùng khi translations đã được load xong
getLabel(): string {
return this.translate.instant('i18n_save');
// Kết quả: 'Lưu' (vi) hoặc 'Save' (en)
}
// Với interpolation params
getErrorMessage(maxLength: number): string {
return this.translate.instant('i18n_message_error_input_max_length', { number: maxLength });
// Kết quả: 'Độ dài tối đa 100 kí tự'
}
// Bất đồng bộ (async) — tự động cập nhật khi đổi ngôn ngữ
getReactiveLabel(): void {
this.translate.get('i18n_search')
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((label: string) => {
console.log(label);
});
}
}6. Global Interpolation — biến dùng chung cho toàn bộ chuỗi dịch
// app.component.ts hoặc main.ts
import { setInterpolateParamDefault } from '@libs-ui/services-translate';
// Đặt một lần — áp dụng tự động cho TẤT CẢ chuỗi dịch
setInterpolateParamDefault({
brand: 'Mobio CRM',
hotline: '1900 1234',
currency: 'VNĐ',
});Trong file dịch JSON, dùng biến global trực tiếp:
{
"i18n_contact_us": "Liên hệ {brand} qua hotline {hotline}",
"i18n_price_unit": "Đơn vị: {currency}"
}Trong template — không cần truyền params:
<span>{{ 'i18n_contact_us' | translate }}</span>
<!-- Kết quả: "Liên hệ Mobio CRM qua hotline 1900 1234" -->7. MessageFormat — Plural và Select
File dịch vi.json:
{
"i18n_user_count": "Có {value} {value,plural, =0 {người dùng} =1 {người dùng} other {người dùng}}",
"i18n_status_display": "Trạng thái: {status,select, active {Đang hoạt động} inactive {Không hoạt động} other {Không xác định}}"
}File dịch en.json:
{
"i18n_user_count": "{value} {value,plural, =0 {user} =1 {user} other {users}} total",
"i18n_status_display": "Status: {status,select, active {Active} inactive {Inactive} other {Unknown}}"
}Trong template:
<!-- Plural -->
<span>{{ 'i18n_user_count' | translate: { value: 5 } }}</span>
<!-- Kết quả (vi): "Có 5 người dùng" -->
<!-- Select -->
<span>{{ 'i18n_status_display' | translate: { status: 'active' } }}</span>
<!-- Kết quả (vi): "Trạng thái: Đang hoạt động" -->Methods
| Method | Signature | Mô tả |
|---|---|---|
| getConfigTranslate | (linkFileI18n?: string) => Provider[] | Trả về mảng providers để cấu hình i18n toàn app. linkFileI18n là đường dẫn thư mục chứa file dịch (mặc định /i18n/). |
| setInterpolateParamDefault | (interpolate: Record<string, string>) => void | Thiết lập biến nội suy toàn cục áp dụng tự động cho mọi chuỗi dịch khi gọi instant(), get(), getParsedResult(). |
| getInterpolateParamDefault | () => Record<string, string> | Lấy giá trị biến nội suy toàn cục hiện tại. |
| instant | (key: string \| string[], params?) => string \| any | Dịch đồng bộ. Trả về key nếu key rỗng (thay vì throw lỗi). Tự động merge interpolateParamDefault. |
| get | (key: string \| string[], params?) => Observable<string \| any> | Dịch bất đồng bộ, tự cập nhật khi đổi ngôn ngữ. Trả về of(key) nếu key rỗng. |
| use | (lang: TYPE_LANGUAGE_SUPPORT) => Observable<any> | Chuyển ngôn ngữ, tự động merge chuỗi dịch nội bộ @libs-ui và lưu vào UtilsCache. |
Types
import { TYPE_LANGUAGE_SUPPORT } from '@libs-ui/interfaces-types';
// TYPE_LANGUAGE_SUPPORT = 'vi' | 'en'TranslateProviderConfig — DI token config, được xuất để sử dụng khi cần override provider thủ công:
import { TranslateProviderConfig } from '@libs-ui/services-translate';Chuỗi dịch nội bộ @libs-ui
LibsUiTranslateService tự động merge hơn 100 key i18n nội bộ dành cho các component @libs-ui (bảng phân trang, upload file, date picker, rich text editor, ...). Các key này được inject khi gọi use() và chỉ điền vào nếu app chưa có sẵn key đó (merge, không ghi đè).
Một số key tiêu biểu:
| Key | vi | en |
|---|---|---|
| i18n_search | Tìm kiếm | Search |
| i18n_save | Lưu | Save |
| i18n_cancel | Hủy bỏ | Cancel |
| i18n_no_data | Không có dữ liệu | No data |
| i18n_add_new | Thêm mới | Add new |
| i18n_delete | Xoá | Delete |
| i18n_valid_empty_message | Trường thông tin không được phép để trống | This field is required |
| i18n_no_result | Không có kết quả | No result |
Lưu ý quan trọng
⚠️ Gọi getConfigTranslate() trong app.config.ts: Bắt buộc cấu hình provider ở cấp app root. KHÔNG gọi trong @NgModule hay lazy feature module.
⚠️ setInterpolateParamDefault() phải gọi trước use(): Biến global chỉ có hiệu lực với các lần dịch sau khi được thiết lập. Nên gọi trong AppComponent.ngOnInit() hoặc main.ts.
⚠️ instant() chỉ hoạt động sau khi translations đã load: Nếu gọi quá sớm (trước khi file JSON load xong), instant() trả về key gốc. Dùng get() (Observable) để đảm bảo luôn có giá trị đúng.
⚠️ Không cần inject LibsUiTranslateService trực tiếp: Service được cung cấp qua DI token của TranslateService. Inject bình thường bằng inject(TranslateService) — Angular sẽ resolve ra LibsUiTranslateService.
⚠️ setTranslation(lang, data, true) — tham số true bắt buộc khi merge: Không truyền true sẽ ghi đè toàn bộ translations đã có cho ngôn ngữ đó, bao gồm cả các key nội bộ của @libs-ui.
Demo
npx nx serve core-uiTruy cập: http://localhost:4500/services/translate
