Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add locale selector #5201

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nertc
Copy link
Contributor

@nertc nertc commented Sep 12, 2024

This PR addresses "Language switcher on the homepage" issue mentioned in #1115

This PR is mainly based on #5077, #3618 and #4461. As solution using Wikimedia selector added external files, many lines of CSS and JS code, this version takes inspiration from #4461 and creates a reusable language selector using <select> tag.

Language selector button, to be consistent, is not hidden when the user is logged in. Instead, it redirects to the "My Preferences" page, where user can change language preferences. When the user is logged out, clicking language selector opens language selection menu.

The reason for logged in user redirection instead of any complex functionality of preferred language change while clicking language selector, was compatibility with the PR #4461. After both PRs will be merged, there will be one smooth simple UX flow for logged in users. Plus, it makes finding language selection page much easier for new users.

Default view of the language selector:

357859639-0ca3007a-ff80-480b-8b37-1816193dc377

Opened language selector:

image

Mobile view of the selector:

Screenshot_20240912-144716

This PR has many different visual impacts, so to make description shorter instead of directly adding images, I'll add links to them.

LTR screenshots:

Medium resolution menu
Medium resolution menu hover
Small resolution menu

RTL screenshots:

Default view
Medium resolution menu
Small resolution menu

@nertc nertc mentioned this pull request Sep 12, 2024
@AntonKhorev
Copy link
Collaborator

What's the advantage of adding the language icon to the sprite map (three times) as opposed to rendering an <svg> with fill="currentColor"?

@nertc
Copy link
Contributor Author

nertc commented Sep 16, 2024

@AntonKhorev Currently, as I found in the project, for icons we use .icon class, which uses sprite with background-image and background-position properties. Adding icon to the sprite was solely for the accordance with the current state and practices of the project.

@nertc nertc force-pushed the issues_1115_add_locale_selector branch from 64c4f7e to e02866a Compare September 16, 2024 06:11
@nertc
Copy link
Contributor Author

nertc commented Sep 16, 2024

Thank you for the review

@tomhughes I've updated accordingly
@AntonKhorev If it's okay to directly use SVG instead of the .icon class practice of the project, I am all for it. I think it's even better solution and produces cleaner, more reusable code.

@AntonKhorev
Copy link
Collaborator

Adding icon to the sprite was solely for the accordance with the current state and practices of the project.

Currently we use both <svg> and css sprite maps. One problem with sprite maps is that it's more difficult to control the colors of the icons.

app/assets/images/sprite.svg is mainly used for buttons inside Leaflet map views, where we don't entirely control html.

@nertc
Copy link
Contributor Author

nertc commented Sep 16, 2024

@AntonKhorev Okay, thanks. I'll update it to <svg>. It will be more comfortable to reuse and will produce cleaner code.

@nertc nertc force-pushed the issues_1115_add_locale_selector branch from e02866a to 713eaaa Compare September 23, 2024 06:07
@nertc
Copy link
Contributor Author

nertc commented Sep 23, 2024

PR was updated to include SVG in the HTML. I tried several different methods to move SVG from sprite, including adding it to the SvgHelper module and adding separate file for it, but current solution turned out to be the most optimal one (SvgHelper had conflicts with the linter because of long lines and separate file was not sufficiently customizable with Bootstrap classes).

<li>
<% if current_user && current_user.id %>
<%= link_to(preferences_path) do %>
<%= render "shared/language_selector", :hoverable => true, :black => true, :classes => "dropdown-item", :disabled => true %>
Copy link
Collaborator

@AntonKhorev AntonKhorev Sep 23, 2024

Choose a reason for hiding this comment

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

dropdown-item here doesn't entirely work, and possibly other Bootstrap classes either don't work as expected or are missing. Some related odd behavior of language link/label:

  • not logged in, no compact mode: no focus outline
  • not logged in, compact mode: same + keyboard up/down controls stop working correctly
  • logged in, no compact mode: focus outline is present but non-standard because no nav-link, but if you add it you'll need more css fixes
  • logged in, compact mode: keyboard controls also not working correctly, in a slightly different manner

Does this thing need to be inside the secondary menu, collapsible into "more"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try to solve the focusing issue without any custom classes. In terms of collapsibility of the translation icon, purpose was to take as little space as possible when the screen is smaller. Without collapsing, HTML code will be shorter and with less "if-else"-es.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you still want this icon collapsing into More?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try to move it out of the "More" and test with different resolutions. If it doesn't break any resolution, I'll keep it out of the "More". It will need less HTML code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated this PR. Tried several solutions and current one needed the smallest and cleanest code. Also, one CSS class was added .text-transparent to make text and background of the <select> tag visually hidden but still focusable.

Language selector will no more be moved to the compact menu. Visually it doesn't take much space and contextually it's more relevant to be as an icon than as a compacted menu button.

@nertc nertc force-pushed the issues_1115_add_locale_selector branch from 713eaaa to 34d2e25 Compare September 26, 2024 07:52
@nertc nertc force-pushed the issues_1115_add_locale_selector branch from 34d2e25 to f5754b8 Compare October 7, 2024 08:05
@AntonKhorev
Copy link
Collaborator

Looks like the languages you get from Language don't quite match the ones expected by Locale.list. For example, we have pt-BR and pt in Language but pt and pt-PT in translations. As a result you can't select European Portuguese.

@nertc nertc force-pushed the issues_1115_add_locale_selector branch 2 times, most recently from 41cb49e to 80708ff Compare October 17, 2024 08:49
<% unless disabled %>
<select role="button" class="p-0 position-absolute top-0 start-0 w-100 h-100 language-change-trigger text-transparent bg-transparent <%= classes %>">
<% Locale.available
.map { |locale| Language.find_by(:code => locale.to_s) }
Copy link
Collaborator

Choose a reason for hiding this comment

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

You'll get a ton of db queries:

  Language Load (0.2ms)  SELECT "languages".* FROM "languages" WHERE "languages"."code" = $1 LIMIT $2  [["code", "af"], ["LIMIT", 1]]
  ↳ app/views/shared/_language_selector.html.erb:8
  Language Load (0.3ms)  SELECT "languages".* FROM "languages" WHERE "languages"."code" = $1 LIMIT $2  [["code", "ar"], ["LIMIT", 1]]
  ↳ app/views/shared/_language_selector.html.erb:8
  Language Load (0.3ms)  SELECT "languages".* FROM "languages" WHERE "languages"."code" = $1 LIMIT $2  [["code", "az"], ["LIMIT", 1]]
  ↳ app/views/shared/_language_selector.html.erb:8
  Language Load (0.3ms)  SELECT "languages".* FROM "languages" WHERE "languages"."code" = $1 LIMIT $2  [["code", "be"], ["LIMIT", 1]]
  ↳ app/views/shared/_language_selector.html.erb:8
  Language Load (0.3ms)  SELECT "languages".* FROM "languages" WHERE "languages"."code" = $1 LIMIT $2  [["code", "bg"], ["LIMIT", 1]]
  ↳ app/views/shared/_language_selector.html.erb:8
...

Comment on lines 7 to 12
<% Locale.available
.map { |locale| Language.find_by(:code => locale.to_s) }
.select { |locale| locale }
.sort_by { |locale| locale[:english_name] }
.each do |language| %>
<option class="form-select" value="<%= language.code %>" <%= "selected" if I18n.locale.to_s == language.code %>><%= language.name %></option>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Using Language table is probably a bad idea. I tried this:

Suggested change
<% Locale.available
.map { |locale| Language.find_by(:code => locale.to_s) }
.select { |locale| locale }
.sort_by { |locale| locale[:english_name] }
.each do |language| %>
<option class="form-select" value="<%= language.code %>" <%= "selected" if I18n.locale.to_s == language.code %>><%= language.name %></option>
<% Locale.available
.select { |locale| I18n.exists? "shared.language_selector.language", :locale => locale, :fallback => false }
.sort_by { |locale| locale.to_s == "en" ? "English" : t(".language", :locale => locale) }
.each do |locale| %>
<option class="form-select" value="<%= locale.to_s %>" <%= "selected" if I18n.locale.to_s == locale.to_s %>><%= locale.to_s == "en" ? "English" : t(".language", :locale => locale) %></option>

with this in locale file:

  share:
    language_selector:
      language: THIS LANGUAGE NAME

Now translators have to add whatever is the appropriate representation of their language there, and then it will appear in the selector.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pushed code can be shortened as Language.where(:code => Locale.available.map {|locale| locale.to_s}). This approach will also cause only one DB query. If there is no other reason for language not to be used, I'll push the code with this approach. Translating language names using i18n and not having one source for them, may cause duplicate translations or inconsistency (because of multiple functionalities using language names).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Translating language names using i18n and not having one source for them

i18n is going to be their one source

may cause duplicate translations or inconsistency (because of multiple functionalities using language names).

We already have two different functionalities using two different sets of languages: i18n and languages of diary entries.

@nertc nertc force-pushed the issues_1115_add_locale_selector branch from 80708ff to 25fd157 Compare October 29, 2024 06:20
@nertc
Copy link
Contributor Author

nertc commented Oct 29, 2024

This PR was updated according to the suggestion of @AntonKhorev.

As described in the CONTRIBUTING.md only updated language config was en:

i18n

If you make a change that involve the locale files (in config/locales) then please only submit changes to the en.yml file. The other files are updated via Translatewiki and should not be included in your pull request.

Because of this, currently only English is shown in the list of language selector. At first, I thought to make a list of all language names in the config file, but when translations will be made, there will be a lot of duplication. The only downside of the current solution is that there will be only those languages shown, which have shared.language_selector.language key.

@AntonKhorev
Copy link
Collaborator

The only downside of the current solution is that there will be only those languages shown, which have shared.language_selector.language key.

Those that have translations that are maintained, so not much of a downside.

I also had a more defensive version of that key with language: THIS LANGUAGE NAME but maybe it's not required. It's possible to document strings on translatewiki like this
https://translatewiki.net/wiki/Osm:Site.copyright.native.native_link/qqq

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.

3 participants