From 2625952ce2b364c3090cf356dae19a74f9abe2e3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 22 Apr 2024 11:17:45 +1000 Subject: [PATCH] more work on WHO language support ($1592) --- .../fhir/r5/elementmodel/LanguageUtils.java | 20 ++- .../fhir/r5/renderers/PatientRenderer.java | 2 +- .../hl7/fhir/utilities/i18n/LanguageTag.java | 129 ++++++++++++++++++ .../i18n/subtag/LanguageSubtagRegistry.java | 21 +++ .../fhir/utilities/i18n/subtag/Subtag.java | 8 ++ .../fhir/utilities/json/model/JsonArray.java | 9 ++ 6 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageTag.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java index a454ad9da..b8f4cd014 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java @@ -23,6 +23,7 @@ import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; +import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.AcceptLanguageHeader; @@ -507,5 +508,22 @@ public class LanguageUtils { } return null; } - + + public static boolean switchLanguage(Element e, String lang) { + if (e.getProperty().isTranslatable()) { + String cnt = getTranslation(e, lang); + e.removeExtension(ToolingExtensions.EXT_TRANSLATION); + if (cnt != null) { + e.setValue(cnt); + } + } + if (e.hasChildren()) { + for (Element c : e.getChildren()) { + if (!switchLanguage(c, lang)) { + return false; + } + } + } + return true; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java index 5e174aa0a..8e2cd1b84 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/PatientRenderer.java @@ -338,7 +338,7 @@ public class PatientRenderer extends ResourceRenderer { } if (!anyComplex) { XhtmlNode tr = tbl.tr(); - nameCell(tr, sd.getTitle()+":", sd.getDescription(), sd.getWebPath()); + nameCell(tr, getContext().getTranslated(sd.getTitleElement()), sd.getDescription(), sd.getWebPath()); XhtmlNode td = tr.td(); td.colspan("3"); if (list.size() == 1) { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageTag.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageTag.java new file mode 100644 index 000000000..c102b606d --- /dev/null +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageTag.java @@ -0,0 +1,129 @@ +package org.hl7.fhir.utilities.i18n; + +import java.util.ArrayList; +import java.util.List; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.i18n.subtag.LanguageSubtagRegistry; + +public class LanguageTag { + + private String code; + private String language; + private String script; + private String region; + private String variant; + private String extension; + private List extLang; + private List privateUse; + private LanguageSubtagRegistry registry; + + public LanguageTag(LanguageSubtagRegistry registry, String code) { + this.registry = registry; + this.code = code; + + if (!Utilities.noString(code)) { + String[] parts = code.split("\\-"); + int c = 0; + int t = parts.length; + if (!registry.hasLanguage(parts[c])) { + throw new FHIRException("Invalid Language code '"+parts[c]+'"'); + } else { + language = parts[c]; + c++; + for (int i = 1; i <= 3; i++) { + if (c < t && registry.hasExtLanguage(parts[c])) { + if (extLang == null) { + extLang = new ArrayList<>(); + } + extLang.add(parts[c]); + c++; + } + } + + if (c < t && registry.hasScript(parts[c])) { + script = parts[c]; + c++; + } + + if (c < t && registry.hasRegion(parts[c])) { + region = parts[c]; + c++; + } + if (c < t && registry.hasVariant(parts[c])) { + variant = parts[c]; + c++; + } + while (c < t && parts[c].startsWith("x")) { + if (privateUse == null) { + privateUse = new ArrayList<>(); + } + privateUse.add(parts[c]); + c++; + } + if (c < t) { + throw new FHIRException( "Unable to recognise part "+(c+1)+" ('"+parts[c]+"') as a valid language part"); + } + + } + } + } + + public String getCode() { + return code; + } + + public String getLanguage() { + return language; + } + + public String getScript() { + return script; + } + + public String getRegion() { + return region; + } + + public String getVariant() { + return variant; + } + + public String getExtension() { + return extension; + } + + public List getExtLang() { + return extLang; + } + + public List getPrivateUse() { + return privateUse; + } + + public String present() { + StringBuilder b = new StringBuilder(); + b.append(registry.getLanguage(language).getDisplay()); + if (region != null) { + b.append("/"); + b.append(registry.getRegion(region).getDisplay()); + } + if (script != null || variant != null) { + CommaSeparatedStringBuilder cb = new CommaSeparatedStringBuilder(); + if (script != null) { + cb.append("Script="+ registry.getScript(script).getDisplay()); + if (variant != null) { + cb.append("Variant="+ registry.getVariant(variant).getDisplay()); + } + b.append(" ("); + b.append(cb.toString()); + b.append(")"); + } + } + return b.toString(); + + } + +} diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/LanguageSubtagRegistry.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/LanguageSubtagRegistry.java index bd4137a86..3cb7c1ef0 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/LanguageSubtagRegistry.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/LanguageSubtagRegistry.java @@ -88,8 +88,29 @@ public class LanguageSubtagRegistry { public boolean containsVariant(String key) { return variants.containsKey(key); } + public VariantSubtag getVariant(String key) { return variants.get(key); } + + public boolean hasLanguage(String subTag) { + return languages.containsKey(subTag); + } + + public boolean hasExtLanguage(String subTag) { + return extLangs.containsKey(subTag); + } + + public boolean hasScript(String subTag) { + return scripts.containsKey(subTag); + } + + public boolean hasRegion(String subTag) { + return regions.containsKey(subTag); + } + + public boolean hasVariant(String subTag) { + return variants.containsKey(subTag); + } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/Subtag.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/Subtag.java index 3b3b31086..98d748c8b 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/Subtag.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/subtag/Subtag.java @@ -42,4 +42,12 @@ public abstract class Subtag { public List getComments() { return List.copyOf(comments); } + + public String getDisplay() { + if (descriptions.size() == 0) { + return ""; + } else { + return descriptions.get(0); + } + } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/model/JsonArray.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/model/JsonArray.java index b1b9d39d5..6ca5a0a99 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/model/JsonArray.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/model/JsonArray.java @@ -145,5 +145,14 @@ public class JsonArray extends JsonElement implements Iterable { items.remove(e); } + + public boolean has(String key) { + for (JsonElement e : items) { + if (e.isJsonString() && key.equals(e.asString())) { + return true; + } + } + return false; + } }