Skip to content

Conversation

@dgdavid
Copy link
Contributor

@dgdavid dgdavid commented May 14, 2025

Problem

Agama offers two different localization (l10n) configurations:

  • One for the installer interface (language and keyboard layout).
  • Another for the installed product (language, keyboard layout, and timezone).

Despite being configured in different UI places, users often confuse these settings. This leads to complaints when changing the "product" language or keyboard layout, but still seeing the installer interface remain unchanged.

Situation is particularly problematic with keyboard layouts, especially when it comes to a password input.

Solution

After considering and rejecting the idea of unifying both settings since it could introduce more complexity and be harder to explain to users, this PR improves discoverability and clarity of installer localization settings:

  • Installer l10n settings are now shown in the top bar, similar to how some desktop environments do. Users can change installer language and keyboard layout from there.
  • When possible, these settings can also be reused for the product to install.
  • In modal dialogs holding a password input a keyboard layout selector is displayed, since these dialogs block access to the top bar.
  • Some reminders are added near password inputs:
    • Current keyboard layout ( (in local connections only).
    • CAPS LOCK warning, if active.
  • Keymap settings are only shown for local connections, since remote sessions depend on the client system’s settings.

Testing

  • Added a unit test.
  • Manually tested various scenarios.

Notes for Reviewers

Due to the wide range of variations and UI paths, screenshots would be impractical. Manual testing is encouraged as part of the review process, for which you can use the image available at https://build.opensuse.org/project/show/systemsmanagement:Agama:branches:lang-and-keyboard-improvements

Scenarios to Test

Local Connection

  • Top bar button shows both installer language and keyboard layout.
  • LuksActivationQuestion shows a keyboard layout button, with reusability depending on whether the product was already selected.
  • QuestionWithPassword dialogs show a layout change button.
  • If product has been selected, these dialogs open by above actions should allows not only changing installer l10n settings but also reusing them for the product.
  • Password input show:
    • Layout reminder.
    • CAPS LOCK warnings (if active).
  • Localization page includes an explanation about there are product settings, with a link to installer settings.
  • Password input at login page only shows the CAPS LOCK warning.

Remote Connection

  • Top bar button shows installer language only.
  • Dialog open by button at top bar only allows language change with explanation that keyboard layout cannot be changed remotely.
  • If product has been selected, installer settings offer the option to reuse the language for the product to install too.
  • LuksActivationQuestion and QuestionWithPassword do not show layout buttons.
  • Localization page behaves the same (with explanation and link).
  • Password inputs:
    • Do not show layout reminders.
    • Show CAPS LOCK warning (if active).
  • Login page shows CAPS LOCK warning only.

Screenshots

Click to show/hide a bunch of them

IMPORTANT: screenshots below might be outdated as a result of changes requested by reviewers before merging


Screen Shot 2025-05-16 at 09 20 49
Screen Shot 2025-05-16 at 09 25 53
Screen Shot 2025-05-16 at 09 20 57
Screen Shot 2025-05-16 at 09 21 02
Screen Shot 2025-05-16 at 09 24 58
Screen Shot 2025-05-16 at 09 21 34
Screen Shot 2025-05-16 at 09 22 12

dgdavid added 16 commits May 12, 2025 08:23
Place a button in the top bar near the main action for opening the
current "Installer options" dialog, making the display language and
keyboard layout settings more discoverable. The button shows visual
hints about the action and displays the current values for both
settings.

Further A11y improvements are needed to ensure non-visual users can
easily access this information as well.
Introduce a checkbox in the installer options dialog to quickly apply
the display language and keyboard layout to the selected product.

Note: Still missing a link to the Localization page.
For not mounting the button to change the language and kebyoard when it
does not make sense.
The Header component now requires the `{ withL10n: true }` option to
work properly in tests. This commit avoids updating the tests themselves by
mocking the component instead.
Adds a keyboard layout hint next to password fields to reduce the likelihood
of users mistyping their passwords due to an unexpected input language or layout.

This is a proof of concept: currently missing tests and polish, but demonstrates
the intended behavior.
By using a hook borrowed from
https://pietrobondioli.com.br/articles/how-to-get-keylock-state-on-react

As previous commit, this is a proof of concept without tests and  a lot
of room for improvements. and currently is missing tests
To avoid application crashing with

> Error: useInstallerL10n must be used within a InstallerL10nContext
>   at useInstallerL10n (http://localhost:8080/index.js:57706:11)
>   at PasswordInput (http://localhost:8080/index.62413e0223d80064660e.hot-update.js:322:99)

due to the usage of the useInstallerL10n hook.

Moving the keyboard reminder into an isolated internal component prevents the hook from executing
when mounting PasswordInput with the `showKeyboardHint={false}` prop.
To make use of installerRender and withL10n: true option for these
components using a PasswordInput.
Enhances the InstallerOptions component to support selective rendering
of settings via a `variant` prop. Allows displaying only language, only
keyboard layout, or both.

Useful in scenarios where only a subset of localization settings needs
to be configured (e.g., keyboard-only setup from a question with
password input)
With the latest changes, it makes more sense for them to live together
in the same module and component. Having them separated was forcing
developers to duplicate logic and props for almost no reason.
The installer doesn't yet support persisting localization settings for
later application when a product is selected. Therefore, it's safer to
show an informative message that might help clarify or even state the
difference between interface and product localization, especially useful
when the installer options dialog is opened before a product has been
chosen.
Since it is only needed to test that it mounts InstallerOptions, not
when its its toggle its rendered or not.
@dgdavid dgdavid changed the title Lang and keyboard improvements feat(web): improve l10n options May 14, 2025
dgdavid added 8 commits May 14, 2025 13:40
Enables passing a custom toggle element for opening the installer
settings panel, which can be helpful for inline toggles or external buttons.

Note: Each toggle renders and manages its own instance of the settings panel.
While multiple panel instances can coexist in the DOM, only one can be open at
a time. This is generally fine but worth keeping in mind for UX and performance.

Alternative approaches that were considered:
- Assigning an `id` to the default toggle and triggering it via
  `document.getElementById(id)?.click()`. This is not idiomatic React
  and could become error-prone if used frequently.
- Using a `ref` on the default toggle and storing it in global context.
  This looks more aligned with React principles, but we still need to explore
  the best practices around it.

Overall, this change improves flexibility without breaking existing usage.
To help users understanding that L10n page settings are for the product
to install, not for the installer interface. It provides a link for
directly open the installer options panel from there, but also teachs
them that such an option is available from the top bar.
Currently limited to password input questions only.
Specifically in the explanation text for the checkbox that allows
users to apply the selected settings to both the installer and the
product to install.
The PasswordInput component now supports a reminders prop to control
which keyboard-related hints are shown, such as the current keymap or a
Caps Lock warning.

- Introduced `reminders` prop to control which keyboard-related hints are shown
  ("keymap", "capslock"), replacing the previous `showKeyboardHint` boolean.
- Refactored reminder rendering into dedicated components: KeymapReminder and CapsLockReminder.
- Skipped keymap hint when running over remote connections.
- Updated LoginPage and PasswordAndConfirmationInput to use new `reminders` prop.
- Expanded test coverage for all reminder combinations and edge cases.
Adding a test it was detected and fixed a wrong state change.
@dgdavid dgdavid force-pushed the lang-and-keyboard-improvements branch from 95a0355 to 709bb28 Compare May 15, 2025 21:07
@coveralls
Copy link

coveralls commented May 15, 2025

Coverage Status

coverage: 63.028%. remained the same
when pulling ad9634c on lang-and-keyboard-improvements
into 14f1aaa on master.

@dgdavid dgdavid marked this pull request as ready for review May 16, 2025 07:41
@dgdavid dgdavid changed the title feat(web): improve l10n options feat(web): improve usability of installer L10n settings May 16, 2025
expect(screen.queryByText(/^CAPS LOCK/)).toBeNull();
});

it("allows pikcing only the keymap reminder", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: picking

Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't we have a check for spelling?

Copy link
Contributor Author

@dgdavid dgdavid May 16, 2025

Choose a reason for hiding this comment

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

Yes, we did. But was deactivated for long time and I drop it. I 've, however, an experimental approach in my development setup to check spelling on commit message... Unfortunately, looks like I ignored it here 😓 Thanks for catching it

Copy link
Contributor Author

@dgdavid dgdavid May 16, 2025

Choose a reason for hiding this comment

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

Yes, we did. But was deactivated for long time and I drop it.

See #2158

I 've, however, an experimental approach in my development setup to check spelling on commit message

Which allows not polluting files with cspell directive comments

expect(screen.queryByText(/is on$/)).toBeNull();
});

it("allows pikcing only the caps locsk reminder", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: picking

@joseivanlopez
Copy link
Contributor

The code looks good, but I found some "weird" behaviour while playing with it. It would be easier to arrange a meeting and I will show you it.

BTW, I have run the code directly in my VM because the iso image fails (missing product?):

Software1/Product; interface=org.freedesktop.DBus.Properties; member=Get error_name=
May 16 11:59:28 agama agama-web-server[2925]: response: 400 Bad Request 105.284848ms
May 16 11:59:32 agama agama-web-server[2925]: request: GET /api/software/registration
May 16 11:59:32 agama agama-web-server[2925]: Server return error Agama service error: D-Bus service error: org.freedesktop.DBus.Error.UnknownProperty: Property 'org.opensuse.Agama1.Registration.Url' (on object '/org/opensuse/Agama/Software1/Product') not found; caused by 1 sender=:1.2 -> dest=org.opensuse.Agama.Software1 serial=192 reply_serial= path=/org/opensuse/Agama/Software1/Product; interface=org.freedesktop.DBus.Properties; member=Get error_name=
‣ Type=error  Endian=l  Flags=0  Version=1 Cookie=108  ReplyCookie=192  Timestamp="Fri 2025-05-16 09:59:32.121085 UTC"
  Sender=:1.6  Destination=:1.2
  ErrorName=org.freedesktop.DBus.Error.UnknownProperty  ErrorMessage="Property 'org.opensuse.Agama1.Registration.Url' (on object '/org/opensuse/Agama/Software1/Product') not found; caused by 1 sender=:1.2 -> dest=org.opensuse.Agama.Software1 serial=192 reply_serial= path=/org/opensuse/Agama/Software1/Product; interface=org.freedesktop.DBus.Properties; member=Get error_name="
  UniqueName=:1.6
  MESSAGE "sas" {
          STRING "Property 'org.opensuse.Agama1.Registration.Url' (on object '/org/opensuse/Agama/Software1/Product') not found; caused by 1 sender=:1.2 -> dest=org.opensuse.Agama.Software1 serial=192 reply_serial= path=/org/opensuse/Agama/Software1/Product; interface=org.freedesktop.DBus.Properties; member=Get error_name=";
          ARRAY "s" {
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/ruby-dbus-0.25.0/lib/dbus/object.rb:424:in 'DBus::Object#dbus_lookup_property'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/ruby-dbus-0.25.0/lib/dbus/object.rb:455:in 'block (2 levels) in <class:Object>'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/ruby-dbus-0.25.0/lib/dbus/object.rb:75:in 'Method#call'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/ruby-dbus-0.25.0/lib/dbus/object.rb:75:in 'DBus::Object#dispatch'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/ruby-dbus-0.25.0/lib/dbus/connection.rb:307:in 'DBus::Connection#process'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/ruby-dbus-0.25.0/lib/dbus/connection.rb:52:in 'DBus::Connection#dispatch_message_queue'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/agama-yast-14.devel10.952ad275a/lib/agama/dbus/software_service.rb:71:in 'Agama::DBus::SoftwareService#dispatch'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/agama-yast-14.devel10.952ad275a/lib/agama/dbus/service_runner.rb:56:in 'block (2 levels) in Agama::DBus::ServiceRunner#run'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/eventmachine-1.2.7/lib/em/timers.rb:56:in 'EventMachine::PeriodicTimer#fire'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in 'Method#call'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in 'EventMachine.run_machine'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in 'EventMachine.run'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/agama-yast-14.devel10.952ad275a/lib/agama/dbus/service_runner.rb:54:in 'Agama::DBus::ServiceRunner#run'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/agama-yast-14.devel10.952ad275a/bin/agamactl:65:in 'Object#start_service'";
                  STRING "/usr/lib64/ruby/gems/3.4.0/gems/agama-yast-14.devel10.952ad275a/bin/agamactl:107:in '<top (required)>'";
                  STRING "/usr/bin/agamactl:25:in 'Kernel#load'";
                  STRING "/usr/bin/agamactl:25:in '<main>'";
          };
  };

@joseivanlopez
Copy link
Contributor

The issues we talked about are not introduced by this PR (they are present in master too). I can approve it after fixing the typos and the the changelog conflict.

@joseivanlopez
Copy link
Contributor

BTW, should I open a bug/issue with the detected problems?

dgdavid added 2 commits May 16, 2025 13:40
And also uses the "installer interface" in a text shown to users instead
of only "interface", which could be confusing.

A comment for translators has been also added.
@dgdavid
Copy link
Contributor Author

dgdavid commented May 16, 2025

@joseivanlopez

The issues we talked about are not introduced by this PR (they are present in master too).

Thanks for checking them. Much appreciated.

I can approve it after fixing the typos and the the changelog conflict.

Working on it now

BTW, should I open a bug/issue with the detected problems?

As you wish. Open a bug/issue or directly create a card in our internal Trello board. I've the feeling that latest will be faster.

Copy link
Contributor

@joseivanlopez joseivanlopez left a comment

Choose a reason for hiding this comment

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

LGTM

@dgdavid dgdavid merged commit a4d0b83 into master May 16, 2025
19 checks passed
@dgdavid dgdavid deleted the lang-and-keyboard-improvements branch May 16, 2025 15:46
dgdavid added a commit that referenced this pull request May 19, 2025
Part of #2359: render current
keymap set for installer only in local connections.
@imobachgs imobachgs mentioned this pull request May 26, 2025
imobachgs added a commit that referenced this pull request May 26, 2025
bmwiedemann pushed a commit to bmwiedemann/openSUSE that referenced this pull request May 27, 2025
https://build.opensuse.org/request/show/1280475
by user IGonzalezSosa + anag_factory
- Version 15

- Handle reused MD RAIDs that are already included at the storage
  configuration (gh/agama-project/agama#2346).

- Allow to resolve in web UI software conflicts
  (gh#agama-project/agama#2348)

- Do not crash when navigating to a Wi-Fi network (bsc#1243415)

- Keep margin between sidebar and main content in all breakpoints
  (gh/agama-project/agama#2370).

- Rework the installer l10n settings (gh#agama-project/agama#2359):
  - Improve discoverability of language and keyboard layout settings.
  - Add contextual messages to help users differentiate between
    installer and product localization settings.
  - Add the ability to reuse installer settings for the product
    to install.
  - In local connections, keyboard layout change is now available
    directly from modal dialog
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants