Skip to content

Translating

Some Shadleaf components have built-in texts, such as the “Cancel” button of an alert dialog. Shadleaf ships these texts in the languages listed below, and your application can add a language or reword a text if needed.

Shadleaf puts its messages behind your application’s message source: it becomes the last parent of the messageSource bean, whether Spring Boot configured it (spring.messages.basename) or you declared it. Spring asks your messages first, so a key you define wins; the keys you leave out come from Shadleaf. An application without messages of its own gets Shadleaf’s as its message source.

The request’s locale picks the language, as for your own messages. Spring Boot takes it from the Accept-Language header unless you set spring.web.locale and spring.web.locale-resolver=fixed, or declare a LocaleResolver of your own. A language Shadleaf has no messages for gets English, whatever the server’s own locale is.

Set the page’s language too, on the <html> element of your layout. Shadleaf formats numbers, dates and lists in the browser (charts, the selection count of a table, the file sizes of a file upload) in that language:

<html th:lang="${#locale.toLanguageTag()}">

Keep your own keys in your messages.properties and messages_<language>.properties, as you would without Shadleaf. Leave Shadleaf’s keys out of them: they come from Shadleaf, in the page’s language.

Set this in application.properties:

spring.messages.fallback-to-system-locale=false

Without it, Spring Boot answers a language your application has no file for with the server’s language: on a server in Dutch, a French page gets the texts of your messages_nl.properties, while Shadleaf, which never falls back to the server’s language, writes English. With it off, such a page gets your messages.properties and Shadleaf’s English.

For a language Shadleaf does not ship, add its keys to your file of that language, next to your own keys:

src/main/resources/messages_fr.properties
app.greeting=Bonjour
sl.alert-dialog.cancel=Annuler
sl.pagination.previous=Précédent
sl.pagination.next=Suivant

The list below has every key. A key you leave out shows Shadleaf’s English text.

To word one of Shadleaf’s texts differently, define its key in your file of that language, such as messages_en.properties for English. Not in messages.properties: Spring reads that file for every language, so the new wording would replace Shadleaf’s other translations too. This needs fallback-to-system-locale off, as above; on a server in English, a page in another language Shadleaf ships would read your messages_en.properties otherwise.

An aria-label or the content you give a component replaces its message for that one element, as each component page says.

Some messages have arguments: sl.table.select-row=Select {0} gets the row’s name. A message with arguments is a MessageFormat pattern, in which an apostrophe starts quoted text, so write an apostrophe twice:

sl.file-upload.announce.uploading={0} fichiers en cours d''envoi.
sl.dialog.error=Une erreur s'est produite. Veuillez réessayer.

A message without arguments is used as it is written, with a single apostrophe, as long as spring.messages.always-use-message-format stays off (the default). With it on, every message in your own files is a pattern and needs the doubled apostrophe.

Some messages are filled in by the browser: the selection count of a table (sl.table.selection.count), the number of results and the chips’ remove buttons of a combobox (sl.combobox.results, sl.combobox.remove), the range of a date picker (sl.date-picker.range) and the file upload’s messages with arguments (sl.file-upload.*). The server renders their pattern into the page with {0} left in, and the script replaces it. They are the same keys, translated the same way, but keep their arguments plain: {0}, never {0,number} or {0,choice,...}.

These languages are supported by Shadleaf:

  • English (en)
  • German (de)
  • French (fr)
  • Dutch (nl)

Every key with its English text, generated from the library’s messages.properties, with the comment it has there. The texts are shown as written in the file.

Button

KeyEnglish
sl.button.loadingLoading

Spinner

KeyEnglish
sl.spinner.labelLoading

Form errors

KeyEnglish
sl.form-errors.titleThere is a problem

Dialog

KeyEnglish
sl.dialog.closeClose
sl.dialog.errorSomething went wrong. Please try again.

Alert Dialog

KeyEnglish
sl.alert-dialog.cancelCancel

Toast

KeyEnglish
sl.toaster.labelNotifications (Alt+T)
sl.toast.closeClose

Theme Toggle

KeyEnglish
sl.theme-toggle.labelTheme
sl.theme-toggle.dark-modeDark mode
sl.theme-toggle.lightLight
sl.theme-toggle.darkDark
sl.theme-toggle.systemSystem

Table

KeyEnglish
sl.table.emptyNo results.
sl.table.select-allSelect all rows on this page
sl.table.select-row

{0}: what the row is, e.g. a name. For the row checkboxes: th:aria-label="#{sl.table.select-row(${person.name})}".

Select {0}
sl.table.selection.count

{0}: the selected rows, {1}: the rows on the page. The browser fills them in as the selection changes, so keep them as plain {0} and {1}: no number or choice format.

{0} of {1} selected

Pagination

KeyEnglish
sl.pagination.labelPagination
sl.pagination.previousPrevious
sl.pagination.previous.labelGo to previous page
sl.pagination.nextNext
sl.pagination.next.labelGo to next page
sl.pagination.moreMore pages
sl.pagination.summary

{0} and {1}: the first and the last row on the page, {2}: all rows. A Slice does not know the total.

{0}–{1} of {2}
sl.pagination.summary.slice{0}–{1}
sl.pagination.summary.empty0 of 0
sl.pagination.summary.page

{0}: the page, {1}: the number of pages.

Page {0} of {1}
sl.pagination.summary.page.slicePage {0}
sl.pagination-size.labelRows per page

Sidebar

KeyEnglish
sl.sidebar.labelMain
sl.sidebar.triggerToggle sidebar

Breadcrumb

KeyEnglish
sl.breadcrumb.labelBreadcrumb
sl.breadcrumb.moreMore levels
sl.breadcrumb.more.triggerShow more levels
KeyEnglish
sl.skip-linkSkip to main content

Calendar

KeyEnglish
sl.calendar.previousGo to the previous month
sl.calendar.nextGo to the next month
sl.calendar.monthMonth
sl.calendar.yearYear
sl.calendar.week-numberWeek

Date Picker

KeyEnglish
sl.date-picker.placeholderPick a date
sl.date-picker.dialogChoose a date
sl.date-picker.range

{0} and {1}: the start and the end of a range, each formatted for the locale.

{0} – {1}

Combobox

KeyEnglish
sl.combobox.emptyNo items found.
sl.combobox.clearClear
sl.combobox.toggleShow options
sl.combobox.remove

{0}: a chosen option's text, on the button of its chip.

Remove {0}
sl.combobox.results.one

The number of options that match, announced while typing. The browser fills in {0}, so keep it as a plain {0}: no number or choice format.

1 result
sl.combobox.results{0} results
sl.combobox.searchingSearching…
sl.combobox.search-errorThe options could not be loaded.

User Menu

KeyEnglish
sl.user-menu.trigger

{0}: the signed-in user's name. The name of sl:user-menu's button in the header, which shows only the picture.

Account: {0}
sl.user-menu.sign-inSign in
sl.user-menu.sign-outSign out

File Upload

KeyEnglish
sl.file-upload.title

sl:file-upload. The title and the Browse button, for a multiple and a single (.one) upload.

Upload files
sl.file-upload.title.oneUpload a file
sl.file-upload.browseBrowse files
sl.file-upload.browse.oneBrowse
sl.file-upload.remove

The browser fills in {0} and {1}, so keep them plain: no number or choice format. {0}: a file's name.

Remove {0}
sl.file-upload.dismissDismiss {0}
sl.file-upload.rejected.type

Why a dropped or picked file was not added. {0}: the max-size, formatted in the page's language ("10 MB").

File type not accepted
sl.file-upload.rejected.sizeLarger than {0}
sl.file-upload.rejected.countOnly one file can be added
sl.file-upload.announce.added

Announced after a change. {0}: the files' names, as a list in the page's language; {1}: why they were not added.

Added {0}.
sl.file-upload.announce.removedRemoved {0}.
sl.file-upload.announce.rejectedCould not add {0}: {1}.
sl.file-upload.cancel

Sending the files with htmx: the button that stops it, and each file's description while it goes. {0}: the percentage sent, formatted in the page's language ("64%").

Cancel upload
sl.file-upload.uploadingUploading · {0}
sl.file-upload.uploading.indeterminateUploading
sl.file-upload.processingProcessing
sl.file-upload.failedUpload failed
sl.file-upload.doneUploaded
sl.file-upload.announce.uploading

Announced when the upload starts and ends. {0}: the number of files, or for one file (.one) its name.

Uploading {0} files.
sl.file-upload.announce.uploading.oneUploading {0}.
sl.file-upload.announce.uploadedUploaded {0} files.
sl.file-upload.announce.uploaded.oneUploaded {0}.
sl.file-upload.announce.failedThe files could not be uploaded. Try again.
sl.file-upload.announce.cancelledUpload cancelled.

A new language is a shadleaf/messages_<language>.properties in the starter (src/main/resources/shadleaf/), with every key of messages.properties. ShadleafMessageBundlesTest checks that each translation has exactly the English keys, the same arguments, and its apostrophes written for whether the message has arguments. Pull requests are welcome.