Skip to content

#1936: Improve localization of gui#1979

Open
KarimALotfy wants to merge 62 commits into
devonfw:mainfrom
KarimALotfy:feature/1936-improve-localization-of-gui
Open

#1936: Improve localization of gui#1979
KarimALotfy wants to merge 62 commits into
devonfw:mainfrom
KarimALotfy:feature/1936-improve-localization-of-gui

Conversation

@KarimALotfy

@KarimALotfy KarimALotfy commented May 27, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #1936

Implemented changes:

  • Add interactive i18n support: app can switch locales (English/Deutsch) via combo box
  • I18nService manages locale state with listener notifications for UI text updates
  • MainController registers locale change listener to refresh all UI labels and prompts on language switch

Testing instructions

Please add conscise, understandable instructions on how a reviewer can test/verify the functionality of your contribution here:

  1. Run in /ide-gui AppLauncher , interact with the third combo box (Language) -- check if language is correctly switched
  2. Add and remove properties to messages.properties and messages_de.properties--> run I18nServiceTest

Checklist for this PR

Make sure everything is checked before merging this PR. For further info please also see
our DoD.

  • When running mvn clean test locally all tests pass and build is successful
  • PR title is of the form #«issue-id»: «brief summary» (e.g. #921: fixed setup.bat). If no issue ID exists, title only.
  • PR top-level comment summarizes what has been done and contains link to addressed issue(s)
  • PR and issue(s) have suitable labels
  • Issue is set to In Progress and assigned to you or there is no issue (might happen for very small PRs)
  • You followed all coding conventions
  • You have added the issue implemented by your PR in CHANGELOG.adoc unless issue is labeled
    with internal
  • You have formulated clear instructions on how to test your contribution under "Testing instructions"

laim2003 added 30 commits March 27, 2026 17:52
- Added logging to IdeGuiStateManager.
- Added functionality, that selecting a different project now switches the IdeContext to the new project.
- Added logging to IdeGuiStateManager.
- Added functionality, that selecting a different project now switches the IdeContext to the new project.
- Added functionality, that selecting a different project now switches the IdeContext to the new project.
- added DI for IdeGuiStateManager.switchContext
…reading the list of workspaces/projects instead of reading those from the UI
…nager, when switchContext(Path rootDirectory, ...) is called.
…tateManager is now set when calling getInstance(), allowing us to provide a getInstance() method with a DI parameter
@coveralls

coveralls commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 28107858485

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage decreased (-0.08%) to 71.305%

Details

  • Coverage decreased (-0.08%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 55 coverage regressions across 4 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

55 previously-covered lines in 4 files lost coverage.

File Lines Losing Coverage Coverage
com/devonfw/ide/gui/MainController.java 27 59.0%
com/devonfw/ide/gui/App.java 26 0.0%
com/devonfw/tools/ide/variable/IdeVariables.java 1 94.34%
com/devonfw/tools/ide/version/VersionSegment.java 1 89.24%

Coverage Stats

Coverage Status
Relevant Lines: 16433
Covered Lines: 12225
Line Coverage: 74.39%
Relevant Branches: 7352
Covered Branches: 4735
Branch Coverage: 64.4%
Branches in Coverage %: Yes
Coverage Strength: 3.15 hits per line

💛 - Coveralls

@KarimALotfy KarimALotfy moved this from 🆕 New to 🏗 In progress in IDEasy board May 27, 2026
@KarimALotfy KarimALotfy self-assigned this May 27, 2026
@hohwille hohwille moved this from 🏗 In progress to Team Review in IDEasy board May 28, 2026
@hohwille hohwille moved this from Team Review to 🏗 In progress in IDEasy board May 28, 2026
@laim2003 laim2003 self-assigned this Jun 16, 2026
@laim2003 laim2003 self-requested a review June 16, 2026 13:20
@KarimALotfy KarimALotfy moved this from 🏗 In progress to Team Review in IDEasy board Jun 16, 2026

@laim2003 laim2003 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! just left some thoughts😊

Comment thread gui/src/main/java/com/devonfw/ide/gui/i18n/I18nService.java Outdated
Comment on lines +51 to +57
public static synchronized I18nService getInstance() {

if (instance == null) {
return getInstance(null);
}
return instance;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hohwille said that we should avoid Singleton patterns where possible and use Dependency Injection. I know i used this pattern a lot in my own state management implementation, but I will also be moving away from Singeltons there. Maybe we can discuss about whether its okay to use them in some instances in the Next daily, probably would also help me clear up some questions. I would probably leave this how it is here for now, but then open a second issue for the future where we update this to DI.

Comment thread gui/src/main/java/com/devonfw/ide/gui/i18n/I18nService.java Outdated
Comment thread gui/src/main/java/com/devonfw/ide/gui/i18n/I18nService.java Outdated
@hohwille hohwille moved this from Team Review to 👀 In review in IDEasy board Jun 22, 2026
@hohwille hohwille added this to the release:2026.07.001 milestone Jun 22, 2026
@@ -0,0 +1,17 @@
# IDEasy GUI - English (default) Translation File

@hohwille hohwille Jun 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cli we named the folder nls for "Native Language Support".
For homogeneity, it would be nice to have both folder named the same.
If the team better understands localization we can also change nls but it is short and maybe more speaking than l10n what other projects use as shortcut for localization.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So should localization be changed to nls , and accordingly the usage of the service also to nls ?

Comment thread gui/src/main/resources/localization/messages.properties Outdated
public void testGetInstanceSingleton() {

LocalizationService service1 = LocalizationService.getInstance(Locale.ENGLISH);
LocalizationService service2 = LocalizationService.getInstance();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better do not rely on default locale in JUnits.
If the user has a different local configured than English, the test will fail.
This will happen sooner or later and cause confusion if some developers have red tests and others have green tests.

service.setLocale(Locale.GERMAN);

assertThat(service.getLocale().getLanguage()).isEqualTo("de");
assertThat(service.getResourceBundle()).isNotNull();

@hohwille hohwille Jun 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also expect that we lookup some key for German and assert the expected translation.
If you choose a generic key that will not likely change, the test should also stay stable.

Comment on lines +21 to +29
@BeforeEach
public void setUp() {

// Reset the singleton instance before each test
LocalizationService.resetInstance();
// Preload English bundle keys used by bundle-completion tests
ResourceBundle englishBundle = ResourceBundle.getBundle("localization.messages", Locale.ENGLISH);
this.englishKeys = extractKeys(englishBundle);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only needed for testing the German keys match the English keys.
I usually expect that setup method does some generic setup used for all test methods.
Nothing wrong here but you could simply do a single test that checks that English keys and German keys are identical and then move this from setup to that single test method.
Surely nice to have...


// Initialize language choices
selectedLanguage.getItems().clear();
selectedLanguage.getItems().addAll("English", "Deutsch");

@hohwille hohwille Jun 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not hardcode the lanugages.
Instead simply add a key like CurrentLanguage so e.g.

CurrentLanguage=English (en)

or

CurrentLanguage=Deutsch (de)

Then we can dynamically lookup the available resource bundles and add new languages without changing the code.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: Adding the ISO-Codes in parenthesis improves UX if you see languages that you cannot read but still want to get an idea of. Example

แบบไทย (th)

Comment on lines +114 to +128
// Set Labels
labelProject.setText(localizationService.get("label.project"));
labelWorkspace.setText(localizationService.get("label.workspace"));
labelLanguage.setText(localizationService.get("label.language"));

// Set ComboBox prompts
selectedProject.setPromptText(localizationService.get("prompt.chooseProject"));
selectedWorkspace.setPromptText(localizationService.get("prompt.chooseWorkspace"));
selectedLanguage.setPromptText(localizationService.get("prompt.chooseLanguage"));

// Set Button texts
androidStudioOpen.setText(localizationService.get("button.open"));
eclipseOpen.setText(localizationService.get("button.open"));
intellijOpen.setText(localizationService.get("button.open"));
vsCodeOpen.setText(localizationService.get("button.open"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also looks unmaintainable.
We cannot hard-code all labels, buttons, etc. of all dialogues here.

KISS solution: To change the language, the GUI has to be restarted.
Otherwise research how to tell JavaFx to reload the messages (or the entire FXML).

Comment on lines +60 to +72
public void setLocale(Locale locale) {

this.locale = locale;
loadBundle();
LOG.info("Locale set to: {}", locale.getLanguage());
for (Runnable listener : this.localeListeners) {
try {
listener.run();
} catch (Exception e) {
LOG.warn("Locale change listener threw: {}", e.getMessage());
}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usually the language is set on operating system level and then Java takes it from there via the default locale. Would be sufficient for KISS. Custom language for IDEasy can IMHO be set via

export IDE_OPTIONS=-Duser.lang=de

in ~/.ide/ide.properties what should already work for the CLI0.
It is nice that you add the option to change in individually in the GUI.
But if you do this, we have to persist the value so that if I close the GUI and reopen it, I do not have to change it every time again.

FYI: If you want to go for KISS, you may keep this out of this first PR and we will add that later. But when we already introduce it, we should IMHO do it properly.

@hohwille hohwille left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KarimALotfy thanks for your PR.
Very nice that you figured out how to do NLS (i18n and l10n) with JavaFx (using Locale, ResourceBundle and the % syntax in fxml files). Great that you also added a proper JUnit test and some advanced features. 👍
I left quite some comments for rework.
In some cases you may decide to go for more KISS and keep some advanced features out of this first PR and introduce in a later improving PR after this one is merged or you should go for the full solution if you already start it.

- Generalize localization keys in the `.properties` files
- Detect available resource bundles automatically instead of hardcoding languages
- Persist the selected language in `~/.ide/ide.properties` using `IDE_LOCALE`
- Load and apply the persisted locale during GUI initialization
- Remove `updateTexts()` from `MainController`
- Reload the app after each language selection to apply localization changes
 - Refactor tests to match the updated localization flow
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GUI Graphical User Interface of IDEasy (aka dashboard) build with JavaFx

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

Improve localization of the GUI

4 participants