Address Hierarchy: Support for i18n?

Hi all, hi @mogoodrich,

Does the Address Hierarchy module support i18n yet, and if not what would be the suggested route to get there?

For labels, values like Location.cityVillage, Location.district, Location.stateProvince,… in your address template would do it since they refer to the keys in the OpenMRS messages.properties file, …I guess you can even refer to keys defined in a your module’s messages.properties file.

Thanks @rubailly.

Yes sure. I was actually rather referring to the address entries themselves. I need to think more about it but I think it would be enough to maintain only two languages: the original (in our case it would be Khmer) and the translation in a language using a Roman alphabet (English to name it).

I don’t know if this is currently possible since the address entries are saved in database. You can check this approach taken in the OpenMRS UI Framework module and see if can be of any help.

I would be good to understand this requirement a bit more @mksd.

The way this module works is that it simply enables populating the patient address columns with pre-created hierarchichal entries. But once the user selects one, and saves it, any link back to the addresshierarchy records is lost. The address elements are simply copied across as text. At least I think this is what happens - please correct if I am not representing this correctly.

So, to have different languages for the address values, would mean that you might store the same address data differently depending on who is using the system.

Can you describe how you would like things to work?

Thanks, Mike

Good point, @mseaton. Yes, unfortunately, there isn’t a key-value relationship between the address hierarchy values and the person address tables in OpenMRS. The raw text is stored in the the person address fields, and so when you edit an existing address it “rebuilds” the hierarchical relationships by text-matching the address fields against the names of the entries in the DB. Not the best way of doing things, but allowed us to incorporate hierarchies without changing the OpenMRS data model–and allows code that is unaware of the hierarchy to just display the address as-is.

One possible approach would be to populate your hierarchy (and therefore your stored person addresses as well) with messages.properties message codes, and then making sure wherever you display addresses you render it with a ui.format or ui.messages. I don’t have a sense of how complex this would be, but there’d probably be some hiccups along the way.

Take care, Mark

Thanks all for your responses.

I guess Mark’s suggestion is the simplest way to get to what we would like to achieve. Instead of importing the actual locations we could import i18n messages. Then it would be up to the address fields widgets to translate them to the UI.

Would you all agree that this outlines a viable plan?

And this would have to work in Bahmni. I haven’t looked at their UI widgets for the address fields to know what that would imply.

Cc: @bharatak, @gsluthra, @darius, thoughts?

@mseaton, @mogoodrich,

In order to achieve this we would like AddressHierarchyAjaxController to return translated entries names where possible. That would be my first idea: using UiUtils#message(..) where necessary in AddressHierarchyAjaxController.

Do you see any drawbacks there? I’m asking because for this I would need to make addresshierarchy-omod depend on uiframework-api, I hope this is ok.

In our instances, we use both uiframework and addresshierarchy, so I don’t think that would be a problem on our end, however I can’t say for everyone, so it would be worth posting a new, separate Talk thread asked about this.

That being said, is there any reason you just can’t go directly to the Core message service? I forget off the top of my head what functionality “UiUtils#message” adds on top of the core messager service.

As for the design itself, I’d have to look deeper, but assumedly you will want to display the translations, yet store the codes in the person address fields? You’d have to have a way to map back from translation to code at some point, right? (Or maybe it’s not a complex as I think, I haven’t looked at the details of the module code in some time).

Take care, Mark

I think going for Core message service would work fine, the only issue being that you may end up having to call Context.getMessageSourceService().getMessage(entry.getName()) in several methods of AddressHierarchyServiceImpl. (…like in this method for instance). Isn’t that a bad design since you would be processing i18n messages at the API (service) layer? or its just Ok?

Correct. I guess that when children are fetched after the parent entry was set (selected by the user), we can assume that there cannot be any duplicates amongst those children. On an example where villages belongs inside districts: there won’t be two villages with the exact same name inside their district. So when a parent is set should be the moment where a translation cache is maintained temporarily (in AddressHierarchyAjaxController) for all its children ; and so on and so forth. Does that make sense? I hope I’m making myself clear…

Regarding UiUtils vs Messager, yes of course you’re right, no need for UiUtils.

No, I’m not really following @mksd… :slight_smile:

But, no worries, as I said, I haven’t looked at the code in quite some time. If you’ve got a clear plan feel free to go ahead and I take a look at the pull request when it’s ready.

Take care. Mark

I “think” I have a plan to ensure that translated messages are displayed to the user. I will ensure that AddressHierarchyAjaxController#getPossibleAddressHierarchyEntriesWithParents(..) returns translations to the UI. However right now I’m stuck at step 0… :no_mouth:

Then the other step is to ensure that when a patient is created/updated, all the address fields get translated back into i18n messages someplace prior to saving the patient. I was thinking of introducing a MethodBeforeAdvice on PatientService#savePatient(Patient). Will I have the locale at hand then? If not is there anywhere else where to intercept the patient before it gets saved?

Actually @rubailly pointed to a member of AddressHierarchyServiceImpl that gave me what I think is a sustainable way to map the person address fields back to their i18n messages. I could use a reverseCache living in AddressHierarchyServiceImpl that would be filled up by AddressHierarchyAjaxController.

Say that this is an address in the en locale:

Province: Mongkol Borei
District: Banteay Neang
Village: Trang 

And that these are the corresponding codes that should be saved:

Province: ah.2.mongkolBorei
District: ah.3.banteayNeang
Village: ah.4.trang

1) In AddressHierarchyAjaxController

The codes are translated to en for the UI display. And each time this happens AddressHierarchyService saves the reverse mapping in reverseCache. Example at the village level:

 "Mongkol Borei - Banteay Neang - Trang" : "ah.4.trang"

The left hand side key, this village string, can be constructed based on the address template definition.

2) Just prior saving a patient (MethodBeforeAdvice)

The village string is reconstructed and the village i18n message is fetched from reverseCache.

Just for completeness, I responded to this point in this thread. There is nothing wrong with translating in the API. This is perfectly normal.

Mike

Thanks for clarifying this @mseaton. So the above logic will remain but all code changes will live in AddressHierarchyServiceImpl. I will follow up very soon as I have now a clear path for implementing i18n support.

Thanks @rubailly for suggesting to hook onto AddressHierarchyServiceImpl in the first place :thumbsup:

So I started introducing address entries names translation in AddressHierarchyServiceImpl and in fact within the unit tests the messages are not being translated. I just added a api/src/test/resources/messages.properties and it seems that this file is ignored.

I tried a few things:

Note that the last point represents a workaround, what I really want to do of course is to load an actual test resource.

Could anyone please look at my last commit and point me to a working solution to load the module’s test messages within the API unit tests? Work in progress branch here: https://github.com/mekomsolutions/openmrs-module-addresshierarchy/commits/MRSCMNS-28

Hi again @mksd. I looked at the commit, and nothing stands out as to why that isn’t working. I’d need to look deeper into the message source code that we have.

I did want to point out that I’ve run into this problem as well, and solved it in the reporting module by introducing a custom message source that extends the one provided by core, to enable any messages defined in various messages.properties files to load. I’m sure it could be improved.

Bigger picture, this should really be something we sort out in the core platform. Is there any reason we shouldn’t just load all message properties found on the classpath during the Spring startup process, keep them in an in-memory cache, and abandon the current approach that depends upon the ModuleFactory start module process and copying message properties files around in temp directories? Maybe this would make overriding message codes more difficult, but I’m sure it’s solvable. @dkayiwa / @raff / @wyclif, thoughts?

Mike

1 Like

@mseaton is this still a problem in platform 2.x? https://tickets.openmrs.org/browse/TRUNK-4097