[FOLIO-546] design i18n for services and data Created: 06/Apr/17  Updated: 12/Nov/18  Resolved: 12/Apr/17

Status: Closed
Project: FOLIO
Components: None
Affects versions: None
Fix versions: None

Type: Umbrella Priority: P3
Reporter: Jakub Skoczen Assignee: Jakub Skoczen
Resolution: Won't Do Votes: 0
Labels: sprint12
Remaining Estimate: Not Specified
Time Spent: 1 hour
Original estimate: Not Specified

Issue links:
Blocks
blocks FOLIO-535 agree on the approach for joining/fil... Open
Sprint:

 Description   

Here's a proposal for i18ing data and services in FOLIO, handling UI string bundles is discussed last.

JSON Schema

Schemas are extended so that all scalar string values that should support i18n are turned into a new type called i18nString, with the following definition:

i18nString.schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "patternProperties": {
    "^[a-z][a-z]$": {"type": "string"}
  }
}

We would then use the schema in place of strings as such:

...
  "displayName": { "$ref": "i18nString"}
...

RAML

In order to filter what languages should be included in the output the service will investigate the Accept-Language request header, eg.

GET /users/1234 HTTP/1.1
Accept-Language: en, da

and reply with:

Content-Language: en, da

{
  "displayName": {
     "en": "Copenhagen",
     "da": "København"
   }
}

Searching/filtering with CQL

For collection services that include CQL filtering/searching capability matching should be performed on all stored i18n values by default, so:

displayName == "Copenhagen"

would return results no matter which internationalized value of displayName matched. Although, what would be included in the body/response still depends on the value of Content-Language header.

To control what internationalized values are used for matching the following query should be specified:

displayName ==/lang=da "Copenhagen"

String bundles

String bundles for things like UI labels that come included with the apps will most likely be pre-loaded into a centralized service. Still, the same general approach described above can be used when programing the API for that centralized translation service.



 Comments   
Comment by Mike Taylor [ 06/Apr/17 ]

That mostly looks good, except that I can't see why the client would ever want to have data returned in more than one language. Can we interpret the Accept-Language header as an ordered list of preferences? Then if I send Accept-Language: en, da and the service has English data, it can just return

Content-Language: en
{
  "displayName": "Copenhagen"
}

and the client's life doesn't get more complicated than it is now.

Comment by Jakub Skoczen [ 07/Apr/17 ]

The client will want the data returned in more than one language when editing the internationalized labels that it maintains on it's own. E.g a Danish library might want to see two input fields when editing Patron Groups, one for the Danish version and one for the English version. This is unrelated to currently selected display language for the UI/client.

Comment by Mike Taylor [ 07/Apr/17 ]

I see. So this is not how you perceive regular day-to-day requests proceeding.

Comment by Marc Johnson [ 07/Apr/17 ]

Jakub Skoczen

What scope/degree of the properties on a resource are you envisaging being able to be expressed in multiple languages (I haven't been involved in the SIG conversations, so am not aware of whether this has been answered)?

It seems to me that for these kinds of resources, many of the properties are likely to be displayed and hence would need to take this form?

Hugs

Comment by Jakub Skoczen [ 07/Apr/17 ]

Marc Johnson This is focused on a general approach for providing just the ability. I will be talking to Filip about how exactly this would be used in the UI. And which fields will potentially needs this capability.

Comment by shale99 [ 09/Apr/17 ]

some thoughts... on both UI and API consumers (i think they should behave the same)

I like the idea of the UI requesting an i18n / l10n bundle via an API for a specific language and locally replacing placeholders in the front end code. In my opinion, this also makes sense to an API user - where they can request a bundle for a specific module in a specific language.
I havent checked how react works with this, but creating a bundle for a module would
1. allow the UI to request this once and then do local lookups - i believe the keys in an i18n bundle should be namespaced, so that patron_group keys. will look something like

module_name.patron_group.on_campus=On Campus

using an api that returns a module specific bundle will allow us to implement a single API for both UI and API consumers - while leaving the display-ablity to the consumer (whether that be an editing app or the UI itself). is there a need to require a module to return specific key / value pairs? wouldnt a returned bundle for a specific module in a specific language suffice? when writing the UI i believe most frameworks works with bundles, i think that when editing , well, this will be via a UI interface as well, so wouldnt an editing app (which could be module agnostic) have it easier to request a bundle from a module and based on metadata in the bundle + key + values - be able to display an editing interface? and then just call a save() API to persist the updated bundle.

Comment by shale99 [ 09/Apr/17 ]

as for the json schema - this looks good to me, i think we only need one returned language value though, this will allow returning a request for GET() items in french, with , for example a material type code 12345567 (not book ) as "Livre" in the item itself instead of Book

Comment by Mike Taylor [ 10/Apr/17 ]

shale99:

module_name.patron_group.on_campus=On Campus

Is "module_name" a placeholder here, and the other components literals? If I understood right, then, the general pattern would be:

module_name.resource_name.value_name=text

and the specific example would be:

users.patron_group.on_campus=On Campus

Is that it?

Comment by Jakub Skoczen [ 10/Apr/17 ]

shale99 I don't think a single centralized "translation" service will not cut it (if that's what you are indeed proposing) as it is largely incompatible with our microservices-like approach. E.g adding or removing a new patron group would effectively be a transaction between the "patron groups" endpoint and the "translations" service. Multiply this by the overall number of all endpoints and you end up with a system where a single service is required to complete any given request.

As I mentioned in the description, there is a place for a centralized service with "static" string bundles (UI labels, messages, etc). Those would get updated when apps are installed/upgraded or when a new string bundle (e.g created by a tenant for a niche language) is explicitly imported into the system. They would also be queries once at the start of the user "sessions" (e.g when user logs in to the UI)

As for why we need to be able to request a list of language rather than just one at a time – see my reply to Mike's comment above.

Comment by Julian Ladisch [ 10/Apr/17 ]

The FOLIO 2018 V1 document lists these use cases: "Ability to supply translations for library-configured content (locations, libraries, permissions, patron group etc.)." However, there won't be much more use cases. This is a very low priority issue and has been excluded from the requirements of version 1. It is very low priority because it is usually done by simply concatenating the names, for example "København/Copenhagen", or even used without translation because it is a small number of names and everybody knowns what they mean.

Comment by Jakub Skoczen [ 10/Apr/17 ]

Agree, this seems to be of a low-priority for V1 so the main purpose is to establish that we can extend the services with a relative easy to provide i18n for values of data elements. Also, it seems that for V1 we would only support a single local/language for tenant thus the need for "Ability..." would be relatively of low priority.

Comment by Julian Ladisch [ 10/Apr/17 ]

I think that the multi-language schema and api capability proposed by this issue is overkill for the long run as well.

Most institutions will never use them. Those that may want to use them can use the translation capabilities instead. Translations of commonly used permission group names and patron group names can be provided by the FOLIO project. An institution that have specific group names or need location or library name translations may create an institution specific language translation file for each module where this is needed and upload those string bundles to the system because these names rarely change. However, this will work for FOLIO specific interfaces only. Standard APIs like NCIP do not support multiple languages and need to use the concatentation of the translations as described above.

In the rare cases where there is really a need to provide several languages at the same time these translations can be retrieved by requesting them one by one.

I don't see the need to introduce an additional level of complexity (array of translations) in most APIs that won't be used and doesn't have advantages.

Comment by shale99 [ 13/Apr/17 ]

basically, i am missing the concept for the end game,
other then
1. translations of course are a must
2. the ui will want to work with bundles
3. modules may have the same names for labels so should be name-spaced

what will this look like
for static and dynamic labels -
1. how do we except users to edit / persist static - dynamic labels - separate interfaces? , a single interface?
2. how do we store them - all labels stored in their respective modules with an api to retrieve / save?
3. if a single UI interface for editing - how does the single interface retrieve ALL available labels from a module? is there a crud to get all dynamic labels and another api to get all static labels from a module - if so, why are two needed?
4. can a api comsumer request a result set in a specific language, which will return translated cv's?

or is there a different line of how translations are going to work

Generated at Thu Feb 08 23:06:35 UTC 2024 using Jira 1001.0.0-SNAPSHOT#100246-sha1:7a5c50119eb0633d306e14180817ddef5e80c75d.