Internationalization
Translate the text that components render, show dates in any locale and lay out pages from right to left.
Overview
bootstrap-ng has no translation files and no global language setting. Most of what users read is your own content, and the few strings that components render themselves, such as a close button's accessible name or a datepicker's month names, come from inputs or from the browser's Intl API. That means it works with Angular's built-in i18n and with runtime translation libraries alike.
- Built-in labels default to English and each has an input to replace it.
- The calendar and datepicker format month names, weekdays and dates with
Intl.DateTimeFormatin the locale you give them. - Bootstrap 6's CSS uses logical properties, so one stylesheet works for both left-to-right and right-to-left pages, and components follow the
dirattribute.
Dates and locales
Set locale on a datepicker or an inline calendar to choose the language of its month names, weekdays and the dates it shows. Each calendar below also sets its own previous and next month labels and the first day of the week:
en-US
de-DE
ja-JP
<div class="d-flex flex-wrap gap-7">
<div lang="en-US">
<p class="fw-bold mb-3">en-US</p>
<div bsCalendar locale="en-US" firstWeekday="0"></div>
</div>
<div lang="de-DE">
<p class="fw-bold mb-3">de-DE</p>
<div
bsCalendar
locale="de-DE"
weekNumbers
weekLabel="Kalenderwoche"
prevLabel="Vorheriger Monat"
nextLabel="Nächster Monat"></div>
</div>
<div lang="ja-JP">
<p class="fw-bold mb-3">ja-JP</p>
<div bsCalendar locale="ja-JP" firstWeekday="0" prevLabel="前の月" nextLabel="次の月"></div>
</div>
</div> Without locale, the calendar uses the browser's language, not Angular's LOCALE_ID. To match the rest of your app, such as dates shown with DatePipe, pass LOCALE_ID in:
import { Component, inject, LOCALE_ID } from '@angular/core';
import { BsDatepicker, BsFormControl } from 'bootstrap-ng';
@Component({
selector: 'app-due-date',
imports: [BsDatepicker, BsFormControl],
template: `<input type="text" bsFormControl bsDatepicker [locale]="locale" aria-label="Due date" />`,
})
export class DueDate {
protected readonly locale = inject(LOCALE_ID);
} A datepicker on an input shows the selected date as an ISO date (YYYY-MM-DD), so what it shows can also be typed. A datepicker on a button shows it in the locale's medium date style. Pass dateFormat with other Intl.DateTimeFormat options, or with a function for full control, to show localized dates in either. The value itself is always an ISO date, whatever the locale:
<!-- Intl.DateTimeFormat options, in the datepicker's locale -->
<input type="text" bsFormControl bsDatepicker locale="en-GB" [dateFormat]="{ dateStyle: 'medium' }" />
<!-- Or a function, such as one that uses Angular's formatDate() -->
<input type="text" bsFormControl bsDatepicker [dateFormat]="format" />import { formatDate } from '@angular/common';
import { BsDateFormatter } from 'bootstrap-ng';
protected readonly format: BsDateFormatter = (date, locale) =>
formatDate(date, 'EEE d MMM y', locale ?? 'en-US');First day of the week and week numbers
Weeks start on Monday by default. Set firstWeekday to 0 to start them on Sunday, as is usual in the United States, Canada and Japan. The first day of the week isn't part of the locale you pass, so set it alongside locale. Where browsers support Intl.Locale.getWeekInfo(), you can read it from the locale:
export class DueDate {
protected readonly locale = inject(LOCALE_ID);
// getWeekInfo() returns 1 to 7 for Monday to Sunday; firstWeekday takes 0 for Sunday.
// Fall back to Monday where Intl.Locale.getWeekInfo() isn't supported.
protected readonly firstWeekday =
(new Intl.Locale(this.locale).getWeekInfo?.().firstDay ?? 1) % 7;
} Add weekNumbers to show a week number before each week. Weeks are numbered with ISO 8601 by default, as is common in Europe; set weekNumbering="simple" to make week 1 the week that holds 1 January instead. Translate the week column's accessible name with weekLabel. See Week numbers on the datepicker page.
Translating labels
Components that render text of their own take it from inputs. Strings that depend on a value, such as a rating's "3 out of 5" or the number of typeahead suggestions, are functions:
<div lang="de" class="d-flex flex-column gap-5">
<div bsAlert theme="success" dismissible closeLabel="Schließen">Änderungen gespeichert.</div>
<nav
bsPagination
aria-label="Seiten"
previousText="Zurück"
nextText="Weiter"
[pageCount]="5"></nav>
<div bsRating aria-label="Bewertung" [value]="4" [valueText]="valueText"></div>
</div>These are the inputs to translate, with links to each component's API:
| Component | Inputs |
|---|---|
| Alert | closeLabel |
| Breadcrumb | label |
| Carousel | pauseLabel and playLabel, and aria-label on bsCarouselItem and bsCarouselIndicator |
| Close button | aria-label |
| Nav overflow | moreText, moreLabel |
| Pagination | previousText, nextText, previousLabel, nextLabel |
| Spinner | label |
| Table | emptyText, loadingText, selectAllLabel, selectHeader, rowLabel, detailHeader, expandLabel, resizeLabel, sortAnnouncement |
| Chips | removeLabel, and aria-label on bsChipDismiss |
| Combobox | countText |
| Datepicker | calendarLabel, and prevLabel, nextLabel and weekLabel on bsCalendar |
| File dropzone | removeLabel, fileListLabel, formatSize, rejectionText |
| Form control | clearLabel |
| Password strength | messages, aria-label |
| Range | formatter |
| Rating | valueText |
| Typeahead | listLabel, resultsText |
Accessible names you give yourself, such as aria-label on a pagination or a carousel, need translating too. When part of a page is in another language than the page itself, mark it with a lang attribute, as the examples on this page do, so screen readers pronounce it correctly.
With Angular i18n
Mark static inputs for translation with Angular's i18n-name attributes, just like native attributes. They work on any input set as a static attribute:
<div bsAlert dismissible closeLabel="Close" i18n-closeLabel i18n>Changes saved.</div>
<nav
bsPagination
aria-label="Pages"
i18n-aria-label
previousText="Previous"
i18n-previousText
nextText="Next"
i18n-nextText
[pageCount]="pageCount()"></nav> For function inputs and strings in TypeScript, use $localize:
protected readonly valueText = (value: number, max: number) =>
$localize`${value}:value: out of ${max}:max: stars`; A localized build sets LOCALE_ID for each language, so passing it to the locale input, as shown above, keeps calendars in the same language as the rest of the build. The file dropzone already formats file sizes with LOCALE_ID. With a runtime library such as Transloco or ngx-translate, bind the same inputs to its translated strings instead.
Right to left
For Arabic, Hebrew, Persian and other right-to-left languages, set dir="rtl" on the <html> element, together with lang:
<!doctype html>
<html lang="ar" dir="rtl">
…
</html> You can also set dir on part of a page. Components read the direction from their element, so arrow keys follow the reading direction in tabs, menus, button group toolbars, ratings and calendars, the carousel slides the other way, popups placed at start or end open on the correct side, and table columns resize from their other edge. Try the arrow keys on the tabs and the rating:
مرحبًا بك في حسابك.
عدّل اسمك وصورتك.
راسلنا في أي وقت.
<div dir="rtl" lang="ar-EG">
<nav bsTabs #tabs="bsTabs" class="nav-tabs mb-3" aria-label="أقسام الحساب">
<button type="button" bsTab="home">الرئيسية</button>
<button type="button" bsTab="profile">الملف الشخصي</button>
<button type="button" bsTab="contact">اتصل بنا</button>
</nav>
<div [bsTabContent]="tabs">
<div bsTabPane="home"><p>مرحبًا بك في حسابك.</p></div>
<div bsTabPane="profile"><p>عدّل اسمك وصورتك.</p></div>
<div bsTabPane="contact"><p>راسلنا في أي وقت.</p></div>
</div>
<div bsRating class="mb-5" aria-label="التقييم" [(value)]="rating" [valueText]="valueText"></div>
<nav
bsPagination
aria-label="الصفحات"
previousText="السابق"
nextText="التالي"
[pageCount]="5"></nav>
</div> Use logical utilities such as ms-*, pe-*, text-start and float-end in your own markup, so it flips with the components. To switch the direction at runtime, for example from a language picker, set both attributes on the document element:
import { Component, DOCUMENT, effect, inject, signal } from '@angular/core';
@Component({ … })
export class App {
private readonly document = inject(DOCUMENT);
protected readonly language = signal<'en' | 'ar'>('en');
constructor() {
effect(() => {
const root = this.document.documentElement;
root.lang = this.language();
root.dir = this.language() === 'ar' ? 'rtl' : 'ltr';
});
}
}