Skip to content

Conversation

@Mr-Imperium
Copy link

TRUNK-6341: Intelligent Locale Matching using RFC 4647

Description of what I changed

I implemented intelligent locale matching to replace the simple, exact-match-only logic in InitializationFilter.

Previously, if a user requested fr-BE (Belgian French) but OpenMRS only had fr (French), the system would default to English. This change uses RFC 4647 (java.util.Locale.lookup) to properly handle:

  • Region Fallbacks: fr-BE now correctly falls back to fr.
  • Quality Weights: Headers like fr;q=0.9, en;q=0.8 are respected.

Specific Changes:

  1. CustomResourceLoader.java:
    • Added findBestMatchLocale(String header) to handle the parsing logic.
    • Added a protected constructor to allow injecting availableLocales. This ensures the logic is testable without needing complex file system mocks (addressing previous review feedback).
  2. InitializationFilter.java:
    • Updated to extract the Accept-Language header and call the new loader method.
  3. CustomResourceLoaderTest.java:
    • Added a new test class with 19 unit tests covering exact matches, fallbacks, quality weights, and edge cases.

Issue I worked on

see https://issues.openmrs.org/browse/TRUNK-6341

Checklist: I completed these to help reviewers :)

  • My IDE is configured to follow the code style of this project.
  • I have added tests to cover my changes.
  • I ran mvn clean package right before creating this pull request and added all formatting changes to my commit.
  • All new and existing tests passed.
  • My pull request is based on the latest changes of the master branch.

availableLocales.add(new Locale("es")); // Spanish
availableLocales.add(new Locale("pt", "BR")); // Portuguese (Brazil)

// Use the protected constructor to inject test locales
Copy link
Contributor

Choose a reason for hiding this comment

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

Kindly as a best practice let’s remove comments that restate what the code already conveys. Clean code is easy to read and maintain.

*
* @param availableLocales the set of locales to be used as available locales
*/
protected CustomResourceLoader(Set<Locale> availableLocales) {
Copy link
Member

Choose a reason for hiding this comment

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

Default visibility is probably the right thing here.

Suggested change
protected CustomResourceLoader(Set<Locale> availableLocales) {
CustomResourceLoader(Set<Locale> availableLocales) {

* @param availableLocales the set of locales to be used as available locales
*/
protected CustomResourceLoader(Set<Locale> availableLocales) {
this.resources = new HashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

Since there's no sensible way to set this, just returning the empty map is probably best.

Suggested change
this.resources = new HashMap<>();
this.resources = Collections.emptyMap();

public Locale findBestMatchLocale(String acceptLanguageHeader) {
// Return default locale if header is null or empty
if (acceptLanguageHeader == null || acceptLanguageHeader.trim().isEmpty()) {
return Locale.ENGLISH;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return Locale.ENGLISH;
return LocaleUtility.getDefaultLocale();

Copy link
Member

Choose a reason for hiding this comment

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

This may slightly disrupt the tests, but comparing assertEquals(Locale.ENGLISH.getLanguage(), findBestMatchLocale(null).getLanguage()); as suggested by the docs should work.

Locale matchedLocale = Locale.lookup(languageRanges, availableLocalesList);

// Return matched locale or default to English if no match found
return matchedLocale != null ? matchedLocale : Locale.ENGLISH;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return matchedLocale != null ? matchedLocale : Locale.ENGLISH;
return matchedLocale != null ? matchedLocale : LocaleUtility.getDefaultLocale();

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