@@ -152,6 +152,7 @@ image::devui_layout_settings.png[Dev UI Settings]
152152The settings page is a dialog that contains settings for Dev UI:
153153
154154- Theme (Change between Desktop/Light/Dark
155+ - Locale (Change to another supported language/locale)
155156- Bugs / Feature requests
156157- Storage (Manage local browser storage used in Dev UI)
157158- Dev MCP (Enable your Dev Mode to expose a MCP Server)
@@ -619,6 +620,214 @@ export class QwcMyExtensionPage extends QwcHotReloadElement {
619620}
620621----
621622
623+ ===== Add i18n to your pages
624+
625+ Quarkus Dev UI supports full internationalization (i18n).
626+ To enable this in your extension, you must externalize all user-visible text into translation files.
627+
628+ All translation files must be placed under:
629+
630+ `<extension>/deployment/src/main/resources/dev-ui/i18n/`
631+
632+ ====== Translation file layout
633+
634+ Each translation file is a plain JavaScript module following one of these naming patterns:
635+
636+ * `languagecode.js`
637+ * `languagecode-COUNTRYCODE.js`
638+
639+ The base *language* file contains translations common to all dialects.
640+ A *country-specific* file contains only the keys that differ from the base.
641+
642+ For example, English:
643+
644+ * `en.js` — base English
645+ * `en-US.js` — American English (only overrides)
646+ * `en-GB.js` — British English (only overrides)
647+
648+ French follows the same pattern:
649+
650+ * `fr.js` — base French
651+ * `fr-FR.js` — France-specific
652+ * `fr-CA.js` — Canada-specific
653+
654+ A list of locale codes can be found here: https://simplelocalize.io/data/locales/
655+
656+ Quarkus Dev UI defaults to English.
657+ If the user’s browser locale is supported, that locale becomes the active default.
658+ Users may also manually change the locale from the Dev UI settings dialog.
659+
660+ ====== Translation file format
661+
662+ Each translation file must export a constant named `templates` containing key–value pairs:
663+
664+ [source,javascript]
665+ ----
666+ export const templates = {
667+ 'myextension-some-key': 'Some translated value'
668+ };
669+ ----
670+
671+ All keys must follow this naming convention:
672+
673+ `<extensionArtifactId>-<id>`
674+
675+ where `<extensionArtifactId>` is taken from the `<artifactId>` of your extension’s runtime module.
676+
677+ Example (Agroal):
678+
679+ [source,xml]
680+ ----
681+ <artifactId>quarkus-agroal</artifactId>
682+ ----
683+
684+ All translation keys must therefore start with:
685+
686+ `quarkus-agroal-`
687+
688+ *Required keys*
689+
690+ 1. Metadata
691+
692+ To translate the extension description (taken from the <description> element of your runtime POM), define:
693+
694+ `<extensionArtifactId>-meta-description`
695+
696+ Example:
697+
698+ [source,javascript]
699+ ----
700+ 'quarkus-agroal-meta-description': 'JDBC Datasources and connection pooling'
701+ ----
702+
703+ 1. Page titles
704+
705+ Each Dev UI page (created with CardPageBuildItem) needs a translation key for its title.
706+
707+ For a page titled "Database view" in Agroal:
708+
709+ [source,java]
710+ ----
711+ cardPageBuildItem.addPage(Page.webComponentPageBuilder()
712+ .icon("font-awesome-solid:database")
713+ .title("Database view")
714+ .componentLink("qwc-agroal-datasource.js"));
715+ ----
716+
717+ The translation key becomes `quarkus-agroal-database_view`.
718+ (for the title, spaces replaced with underscores, lower-case.)
719+
720+ Example entry:
721+
722+ [source,javascript]
723+ ----
724+ 'quarkus-agroal-database_view': 'Database view'
725+ ----
726+
727+ 1. General text
728+
729+ All other user-visible strings in your JS code should also be externalized.
730+
731+ Example:
732+
733+ [source,javascript]
734+ ----
735+ import { str } from '@lit/localize';
736+
737+ export const templates = {
738+ // Metadata
739+ 'quarkus-agroal-meta-description': 'JDBC Datasources and connection pooling',
740+
741+ // Pages
742+ 'quarkus-agroal-database_view': 'Database view',
743+
744+ // General
745+ 'quarkus-agroal-fetching-data-sources': 'Fetching data sources...'
746+ };
747+ ----
748+
749+ *Updating your JavaScript pages*
750+
751+ To use translations inside your Lit components, update your code as follows.
752+
753+ 1. Import the localization helpers
754+
755+ [source,javascript]
756+ ----
757+ import { msg, str, updateWhenLocaleChanges } from 'localization';
758+ ----
759+
760+ - msg: lookup a translation key
761+ - str: create dynamic strings with variables
762+ - updateWhenLocaleChanges: tells Lit to re-render automatically when language changes
763+
764+ 1. Register for locale changes
765+
766+ Add the following in your constructor:
767+
768+ [source,javascript]
769+ ----
770+ constructor() {
771+ super();
772+ updateWhenLocaleChanges(this);
773+ }
774+ ----
775+
776+ 1. Replace inline text with translation lookups
777+
778+ Before:
779+
780+ [source,javascript]
781+ ----
782+ return this._renderProgressBar('Fetching data sources...');
783+ ----
784+
785+ After:
786+
787+ [source,javascript]
788+ ----
789+ return this._renderProgressBar(
790+ msg('Fetching data sources...', { id: 'quarkus-agroal-fetching-data-sources' })
791+ );
792+ ----
793+
794+ If the key does not exist in the current locale, the fallback text ('Fetching data sources...') is used automatically.
795+
796+ Examples:
797+
798+ [source,javascript]
799+ ----
800+ // en.js
801+ 'quarkus-agroal-fetching-data-sources': 'Fetching data sources...'
802+
803+ // fr.js
804+ 'quarkus-agroal-fetching-data-sources': 'Récupération des sources de données…'
805+ ----
806+
807+ 1.1 Using variables inside translations
808+
809+ For dynamic messages, wrap the default string in str:
810+
811+ [source,javascript]
812+ ----
813+ } catch (error) {
814+ notifier.showErrorMessage(
815+ msg(str`Failed to save file: ${error}`, { id: 'quarkus-agroal-file-save-failed' })
816+ );
817+ }
818+ ----
819+
820+ In translation files, use positional variables (${0}, ${1}, …):
821+
822+ [source,javascript]
823+ ----
824+ // en.js
825+ 'quarkus-agroal-file-save-failed': str`Failed to save file: ${0}`,
826+
827+ // fr.js
828+ 'quarkus-agroal-file-save-failed': str`Échec de l’enregistrement du fichier : ${0}`,
829+ ----
830+
622831===== UI Components
623832
624833====== Vaadin Web Components
@@ -770,7 +979,7 @@ this.storageControl.set('height', 123); // Set some val
770979
771980https://github.com/quarkusio/quarkus/blob/main/extensions/devui/resources/src/main/resources/dev-ui/qwc/qwc-footer.js[Example code]
772981
773- ======= Per Application
982+ * Per Application*
774983
775984You can narrow the score of the storage further by the current application:
776985
@@ -779,7 +988,7 @@ You can narrow the score of the storage further by the current application:
779988storageControl = new StorageController(this, true); // Passing in true will scope per application
780989----
781990
782- ======= Storage Setting
991+ * Storage Setting*
783992
784993Users can have a raw view on the storage in the settings page:
785994
0 commit comments