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

Refactor property value internationalization (fixes #3470) #3482

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

jbaiter
Copy link
Collaborator

@jbaiter jbaiter commented Jul 17, 2021

This admittedly huge changeset refactors the way the correct language value for a given property value is determined. A recent change in the Manifesto library made it possible to pass a list of languages (in descending order of preference) to the PropertyValue.getValue and PropertyValue.getValues methods, instead of relying on a single default locale that would be set when parsing the manifest. This allows for a better user experience, since we can now dynamically respond to changes in the user's language preferences without having to re-parse the manifest. Additionally, we can make use of the browser's language preferences to better match the user's language abilities.

However, this required a substantial reworking of quite a few parts of Mirador, namely:

  • Introduced a new config.userLanguages redux state variable that has the list of languages in descending order of preference (as returned by i18next.languages after setting the configured language)
  • Every selector that would previously simply call getValue on a property value was changed to retrieve the user languages from the store and pass them to getValue. Additionally, for property values that are displayed in the companion window, where the locale can be overridden separately from the global language, selectors are extended to take a third overrideLanguages parameter that allows customization of the languages passed to getValue/getValues
  • When changing the global language setting, the value in the companion window's LocalePicker (if enabled) now will select the next-best available language according to the langauge pereferences, if the desired language is not part of the metadata.

As a whole, these changes now allow on-the-fly switching of languages via both the global language selector and the companion window locale picker for all property values parsed from the manifest, with support for falling back according to the user's client language preferences.

I'd be grateful for a careful review, I had quite a bit of trouble keeping all the parts involved in my head, there might be things I have missed or not done as elegant as they could be.

The changes can be tested in this new integration test:
https://deploy-preview-3482--mirador-dev.netlify.app/__tests__/integration/mirador/i18n.html

@codecov-commenter
Copy link

codecov-commenter commented Jul 17, 2021

Codecov Report

Merging #3482 (ea883b6) into master (c941b70) will decrease coverage by 0.10%.
The diff coverage is 90.27%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3482      +/-   ##
==========================================
- Coverage   88.91%   88.80%   -0.11%     
==========================================
  Files         198      198              
  Lines        3409     3439      +30     
==========================================
+ Hits         3031     3054      +23     
- Misses        378      385       +7     
Impacted Files Coverage Δ
src/components/CanvasLayers.js 100.00% <ø> (ø)
src/components/CollectionDialog.js 51.11% <ø> (ø)
src/components/SidebarIndexTableOfContents.js 100.00% <ø> (ø)
src/components/SidebarIndexThumbnail.js 100.00% <ø> (ø)
src/components/WindowSideBarCollectionPanel.js 5.00% <ø> (ø)
src/components/WindowSideBarInfoPanel.js 80.00% <ø> (ø)
src/lib/MiradorCanvas.js 97.67% <ø> (ø)
src/state/actions/action-types.js 100.00% <ø> (ø)
src/state/actions/config.js 83.33% <0.00%> (-16.67%) ⬇️
src/state/reducers/config.js 88.88% <0.00%> (-11.12%) ⬇️
... and 10 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c941b70...ea883b6. Read the comment docs.

@jbaiter jbaiter force-pushed the propval-i18n-refactor branch 2 times, most recently from c0102d0 to f4152fd Compare July 17, 2021 22:30
@jbaiter jbaiter requested a review from cbeer July 18, 2021 16:37
This admittedly huge changeset refactors the way the correct language
value for a given property value is determined. A recent change in the
Manifesto library made it possible to pass a list of languages (in
descending order of preference) to the `PropertyValue.getValue` and
`PropertyValue.getValues` methods, instead of relying on a single
default locale that would be set when parsing the manifest. This allows
for a better user experience, since we can now dynamically respond to
changes in the user's language preferences without having to re-parse
the manifest. Additionally, we can make use of the browser's language
preferences to better match the user's language abilities.

However, this required a substantial reworking of quite a few parts of
Mirador, namely:

- Introduced a new `config.userLanguages` redux state variable that
  has the list of languages in descending order of preference (as
  returned by i18next.languages after setting the configured language)
- Every selector that would previously simply call `getValue` on a
  property value was changed to retrieve the user languages from the
  store and pass them to `getValue`. Additionally, for property values
  that are displayed in the companion window, where the locale can be
  overridden separately from the global language, selectors are extended
  to take a third `overrideLanguages` parameter that allows
  customization of the languages passed to `getValue`/`getValues`
- When changing the global language setting, the value in the companion
  window's `LocalePicker` (if enabled) now will select the next-best
  available language according to the langauge pereferences, if the
  desired language is not part of the metadata.

As a whole, these changes now allow on-the-fly switching of languages
via both the global language selector and the companion window locale
picker for all property values parsed from the manifest, with support
for falling back according to the user's client language preferences.
return (resource
&& resource.getLabel
&& resource.getLabel().length > 0)
? resource.getLabel().getValue()
? resource.getLabel().getValue(langs)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you remind me of the manifesto behavior if there's no label in a given language? Is there a fallback or something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Currently the behavior is to simply pick the first value, with no regard for the (legacy) defaultLocale. I'm still planning on submitting a PR that uses that instead, but haven't gotten around to it yet.

@@ -30,11 +30,11 @@ const reorder = (list, startIndex, endIndex) => {
/** */
export class CanvasLayers extends Component {
/** */
static getUseableLabel(resource, index) {
static getUseableLabel(resource, index, langs) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This probably goes for all these label helpers... I wonder if we should refactor these back into the instance, now that they depend on some state data?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Probably a good idea, yes! Or maybe refactor it into a utility function? I've seen this exact static method (and minor variations of it) in more than a handful components.

? resource.getLabel().getValue()
: String(index + 1);
? resource.getLabel().getValue(langs)
: resource.id;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why resource.id instead of the index value?

Copy link
Collaborator Author

@jbaiter jbaiter Aug 6, 2021

Choose a reason for hiding this comment

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

Sorry about that, it was probably an ill-advised attempt a a unification of the various getUsableLabel methods that went nowhere in the end, I'll revert!

Just checked, index was never passed in this component, so the fallback would always be "NaN" (String(undefined + 1)). Using the resource identifier seems more sensible.

// If `locale` is not among the available locales, it should be the first available
// locale that matches the language preferences from the `userLanguages` store value
if (availableLocales.indexOf(locale) < 0) {
locale = userLanguages.find(l => availableLocales.indexOf(l) >= 0) ?? availableLocales[0];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we push a default fallback locale into settings? It probably shouldn't happen, but 🤷

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well, there's already the language value in the store that determines the language of the whole app, which is included in the userLanguages list. I'm not sure if an additional metadataFallbackLocale really makes sense, wouldn't it be identical to language in most usage scenarios?

@jbaiter jbaiter requested a review from cbeer August 6, 2021 15:35
@jbaiter
Copy link
Collaborator Author

jbaiter commented Nov 19, 2021

Any update on this?

@jbaiter
Copy link
Collaborator Author

jbaiter commented Sep 20, 2022

It's been over a year, any update on this? If this is still considered relevant, I can rebase my changes on top of the latest changes.

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