i18n work in rendering context (WIP) (#1592)

This commit is contained in:
Grahame Grieve 2024-04-09 05:19:25 +10:00
parent 2ce5bbd0d8
commit a111989bb4
8 changed files with 212 additions and 68 deletions

View File

@ -42,6 +42,7 @@ import javax.annotation.Nullable;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -50,7 +51,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class BaseDateTimeType extends PrimitiveType<Date> { public abstract class BaseDateTimeType extends PrimitiveType<Date> {
static final long NANOS_PER_MILLIS = 1000000L; static final long NANOS_PER_MILLIS = 1000000L;
static final long NANOS_PER_SECOND = 1000000000L; static final long NANOS_PER_SECOND = 1000000000L;
private static final Map<String, TimeZone> timezoneCache = new ConcurrentHashMap<>(); private static final Map<String, TimeZone> timezoneCache = new ConcurrentHashMap<>();
@ -816,9 +817,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* {@link #toHumanDisplayLocalTimezone()} instead. * {@link #toHumanDisplayLocalTimezone()} instead.
* </p> * </p>
*/ */
public String toHumanDisplay() { public String toHumanDisplay() {
return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString()); return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString());
} }
public String toHumanDisplay(Locale locale) {
return DateTimeUtil.toHumanDisplay(locale, getTimeZone(), getPrecision(), getValue());
}
/** /**
* Returns a human readable version of this date/time using the system local format, converted to the local timezone * Returns a human readable version of this date/time using the system local format, converted to the local timezone

View File

@ -24,8 +24,11 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.renderers.CodeSystemRenderer.Translateable;
import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.MultiLanguagePolicy;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator;
@ -38,6 +41,27 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class CodeSystemRenderer extends TerminologyRenderer { public class CodeSystemRenderer extends TerminologyRenderer {
public class Translateable {
private String lang;
private StringType value;
public Translateable(String lang, StringType value) {
this.lang = lang;
this.value = value;
}
public String getLang() {
return lang;
}
public StringType getValue() {
return value;
}
}
private Boolean doMarkdown = null; private Boolean doMarkdown = null;
public CodeSystemRenderer(RenderingContext context) { public CodeSystemRenderer(RenderingContext context) {
@ -426,34 +450,47 @@ public class CodeSystemRenderer extends TerminologyRenderer {
if (hasDefinitions) { if (hasDefinitions) {
td = tr.td(); td = tr.td();
if (c != null &&c.hasDefinitionElement()) { if (c != null &&c.hasDefinitionElement()) {
if (getContext().getLang() == null) { // translations of the definition might come from either the translation extension, or from the designations
if (hasMarkdownInDefinitions(cs)) { StringType defn = context.getTranslatedElement(c.getDefinitionElement());
addMarkdown(renderStatusDiv(c.getDefinitionElement(), td), c.getDefinition()); boolean sl = false;
} else { for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
renderStatus(c.getDefinitionElement(), td).addText(c.getDefinition()); if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
sl = true;
} }
} else if (getContext().getLang().equals("*")) { }
boolean sl = false;
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) if (getContext().getMultiLanguagePolicy() == MultiLanguagePolicy.NONE && (sl || ToolingExtensions.hasLanguageTranslations(defn))) {
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) if (hasMarkdownInDefinitions(cs)) {
sl = true; addMarkdown(renderStatusDiv(defn, td), defn.asStringValue());
td.addText((sl ? cs.getLanguage("en")+": " : "")); } else {
if (hasMarkdownInDefinitions(cs)) renderStatus(defn, td).addText(defn.asStringValue());
addMarkdown(renderStatusDiv(c.getDefinitionElement(), td), c.getDefinition()); }
else } else {
renderStatus(c.getDefinitionElement(), td).addText(c.getDefinition()); List<Translateable> list = new ArrayList<>();
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { list.add(new Translateable(cs.getLanguage(), defn));
for (Extension ext : defn.getExtensionsByUrl(ToolingExtensions.EXT_TRANSLATION)) {
list.add(new Translateable(ext.getExtensionString("lang"), ext.getExtensionByUrl("content").getValueStringType()));
}
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
td.br(); list.add(new Translateable(cd.getLanguage(), cd.getValueElement()));
td.addText(cd.getLanguage()+": "+cd.getValue());
} }
} }
} else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) { boolean first = true;
renderStatus(c.getDefinitionElement(), td).addText(c.getDefinition()); for (Translateable ti : list) {
} else { if (first) {
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { first = false;
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) { } else {
td.addText(cd.getValue()); td.br();
}
if (ti.lang != null) {
td.addText(ti.lang + ": ");
}
if (hasMarkdownInDefinitions(cs)) {
addMarkdown(renderStatusDiv(ti.getValue(), td), ti.getValue().asStringValue());
} else {
renderStatus(ti.getValue(), td).addText(ti.getValue().asStringValue());
} }
} }
} }

View File

@ -346,7 +346,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (JurisdictionUtilities.isJurisdiction(system)) { if (JurisdictionUtilities.isJurisdiction(system)) {
return JurisdictionUtilities.displayJurisdiction(system+"#"+code); return JurisdictionUtilities.displayJurisdiction(system+"#"+code);
} }
ValidationResult t = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions().withLanguage(context.getLang()).withVersionFlexible(true), system, version, code, null); ValidationResult t = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions().withLanguage(context.getLocale().toString()).withVersionFlexible(true), system, version, code, null);
if (t != null && t.getDisplay() != null) if (t != null && t.getDisplay() != null)
return t.getDisplay(); return t.getDisplay();

View File

@ -843,8 +843,8 @@ public abstract class ResourceRenderer extends DataRenderer {
} }
public void markLanguage(XhtmlNode x) { public void markLanguage(XhtmlNode x) {
x.setAttribute("lang", context.getLang()); x.setAttribute("lang", context.getLocale().toString());
x.setAttribute("xml:lang", context.getLang()); x.setAttribute("xml:lang", context.getLocale().toString());
x.addTag(0, "hr"); x.addTag(0, "hr");
x.addTag(0, "p").b().tx(context.getLocale().getDisplayName()); x.addTag(0, "p").b().tx(context.getLocale().getDisplayName());
x.addTag(0, "hr"); x.addTag(0, "hr");

View File

@ -6,9 +6,11 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.FHIRFormatError;
@ -18,11 +20,8 @@ import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.BaseDateTimeType;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.PrimitiveType; import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.renderers.utils.Resolver.IReferenceResolver; import org.hl7.fhir.r5.renderers.utils.Resolver.IReferenceResolver;
@ -36,6 +35,40 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.RenderingI18nContext; import org.hl7.fhir.utilities.i18n.RenderingI18nContext;
import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationOptions;
/**
* Managing Language when rendering
*
* You can specify a language to use when rendering resources by setting the setLocale() on
* the super class. The locale drives the following:
* - choice of java supplied rendering phrase, if translations are provided for the locale
* - integer and date formats used (but see below for date formats)
* - automatic translation of coded values, if language supplements are available
* - choosing text representation considering the FHIR translation extension
*
* By default, the locale is null, and the default locale for the underlying system is used.
* If you set locale to a specific value, then that value will be used instead of the default locale.
*
* By default, only a single language is rendered, based on the locale. Where resources contain
* multiple language content (designations in CodeSystem and ValueSet, or using the translation
* extension), you can control what languages are presented using the properties multiLanguagePolicy
* and languages
* - multiLanguagePolicy: NONE (default), DESIGNATIONS, ALL
* - languages: a list of allowed languages. Default is empty which means all languages in scope via multiLanguagePolicy
*
* Managing Date/Time Formatting
*
* This class has multiple parameters that influence date/time formatting when rendering resources
*
* - The default rendering is using the default java locale as above
* - If you setLocale() to something, then the defaults for the locale will be used
* - Else you can set the values of dateTimeFormat, dateFormat, dateYearFormat and dateYearMonthFormat
*
* If you set the value of locale, the values of dateTimeFormat, dateFormat, dateYearFormat and dateYearMonthFormat are
* reset to the system defaults
*
* Timezones: by default, date/times are rendered in their source timezone
*
*/
public class RenderingContext extends RenderingI18nContext { public class RenderingContext extends RenderingI18nContext {
// provides liquid templates, if they are available for the content // provides liquid templates, if they are available for the content
@ -176,6 +209,12 @@ public class RenderingContext extends RenderingI18nContext {
} }
} }
public enum MultiLanguagePolicy {
NONE, // ONLY render the language in the locale
DESIGNATIONS, // in addition to the locale language, render designations from other languages (eg. as found in code systems and value sets
ALL // in addition to translations in designations, look for an render translations (WIP)
}
private IWorkerContext worker; private IWorkerContext worker;
private MarkDownProcessor markdown; private MarkDownProcessor markdown;
private ResourceRendererMode mode; private ResourceRendererMode mode;
@ -185,7 +224,15 @@ public class RenderingContext extends RenderingI18nContext {
private IEvaluationContext services; private IEvaluationContext services;
private ITypeParser parser; private ITypeParser parser;
private String lang; // i18n related fields
private MultiLanguagePolicy multiLanguagePolicy = MultiLanguagePolicy.NONE;
private Set<String> allowedLanguages = new HashSet<>();
private ZoneId timeZoneId;
private DateTimeFormatter dateTimeFormat;
private DateTimeFormatter dateFormat;
private DateTimeFormatter dateYearFormat;
private DateTimeFormatter dateYearMonthFormat;
private String localPrefix; // relative link within local context private String localPrefix; // relative link within local context
private int headerLevelContext; private int headerLevelContext;
private boolean canonicalUrlsAsLinks; private boolean canonicalUrlsAsLinks;
@ -212,11 +259,6 @@ public class RenderingContext extends RenderingI18nContext {
private boolean showComments = false; private boolean showComments = false;
private FhirPublication targetVersion; private FhirPublication targetVersion;
private ZoneId timeZoneId;
private DateTimeFormatter dateTimeFormat;
private DateTimeFormatter dateFormat;
private DateTimeFormatter dateYearFormat;
private DateTimeFormatter dateYearMonthFormat;
private boolean copyButton; private boolean copyButton;
private ProfileKnowledgeProvider pkp; private ProfileKnowledgeProvider pkp;
private String changeVersion; private String changeVersion;
@ -233,13 +275,13 @@ public class RenderingContext extends RenderingI18nContext {
* @param markdown - appropriate markdown processing engine * @param markdown - appropriate markdown processing engine
* @param terminologyServiceOptions - options to use when looking up codes * @param terminologyServiceOptions - options to use when looking up codes
* @param specLink - path to FHIR specification * @param specLink - path to FHIR specification
* @param lang - langauage to render in * @param locale - i18n for rendering
*/ */
public RenderingContext(IWorkerContext worker, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, String lang, ResourceRendererMode mode, GenerationRules rules) { public RenderingContext(IWorkerContext worker, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, Locale locale, ResourceRendererMode mode, GenerationRules rules) {
super(); super();
this.worker = worker; this.worker = worker;
this.markdown = markdown; this.markdown = markdown;
this.lang = lang; this.locale = locale;
this.links.put(KnownLinkType.SPEC, specLink); this.links.put(KnownLinkType.SPEC, specLink);
this.localPrefix = localPrefix; this.localPrefix = localPrefix;
this.mode = mode; this.mode = mode;
@ -247,12 +289,10 @@ public class RenderingContext extends RenderingI18nContext {
if (terminologyServiceOptions != null) { if (terminologyServiceOptions != null) {
this.terminologyServiceOptions = terminologyServiceOptions; this.terminologyServiceOptions = terminologyServiceOptions;
} }
// default to US locale - discussion here: https://github.com/hapifhir/org.hl7.fhir.core/issues/666
this.locale = new Locale.Builder().setLanguageTag("en-US").build();
} }
public RenderingContext copy() { public RenderingContext copy() {
RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, getLink(KnownLinkType.SPEC), localPrefix, lang, mode, rules); RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, getLink(KnownLinkType.SPEC), localPrefix, locale, mode, rules);
res.resolver = resolver; res.resolver = resolver;
res.templateProvider = templateProvider; res.templateProvider = templateProvider;
@ -291,6 +331,8 @@ public class RenderingContext extends RenderingI18nContext {
res.terminologyServiceOptions = terminologyServiceOptions.copy(); res.terminologyServiceOptions = terminologyServiceOptions.copy();
res.typeMap.putAll(typeMap); res.typeMap.putAll(typeMap);
res.multiLanguagePolicy = multiLanguagePolicy;
res.allowedLanguages.addAll(allowedLanguages);
return res; return res;
} }
@ -330,8 +372,16 @@ public class RenderingContext extends RenderingI18nContext {
return markdown; return markdown;
} }
public String getLang() { public MultiLanguagePolicy getMultiLanguagePolicy() {
return lang; return multiLanguagePolicy;
}
public void setMultiLanguagePolicy(MultiLanguagePolicy multiLanguagePolicy) {
this.multiLanguagePolicy = multiLanguagePolicy;
}
public Set<String> getAllowedLanguages() {
return allowedLanguages;
} }
public String getLocalPrefix() { public String getLocalPrefix() {
@ -502,14 +552,6 @@ public class RenderingContext extends RenderingI18nContext {
return ref; return ref;
} }
public RenderingContext setLang(String lang) {
this.lang = lang;
if (lang != null) {
setLocale(new Locale(lang));
}
return this;
}
public RenderingContext setLocalPrefix(String localPrefix) { public RenderingContext setLocalPrefix(String localPrefix) {
this.localPrefix = localPrefix; this.localPrefix = localPrefix;
return this; return this;
@ -746,8 +788,8 @@ public class RenderingContext extends RenderingI18nContext {
public String getTranslated(PrimitiveType<?> t) { public String getTranslated(PrimitiveType<?> t) {
if (lang != null) { if (locale != null) {
String v = ToolingExtensions.getLanguageTranslation(t, lang); String v = ToolingExtensions.getLanguageTranslation(t, locale.toString());
if (v != null) { if (v != null) {
return v; return v;
} }
@ -755,18 +797,32 @@ public class RenderingContext extends RenderingI18nContext {
return t.asStringValue(); return t.asStringValue();
} }
public StringType getTranslatedElement(PrimitiveType<?> t) {
if (locale != null) {
StringType v = ToolingExtensions.getLanguageTranslationElement(t, locale.toString());
if (v != null) {
return v;
}
}
if (t instanceof StringType) {
return (StringType) t;
} else {
return new StringType(t.asStringValue());
}
}
public String getTranslatedCode(Base b, String codeSystem) { public String getTranslatedCode(Base b, String codeSystem) {
if (b instanceof org.hl7.fhir.r5.model.Element) { if (b instanceof org.hl7.fhir.r5.model.Element) {
org.hl7.fhir.r5.model.Element e = (org.hl7.fhir.r5.model.Element) b; org.hl7.fhir.r5.model.Element e = (org.hl7.fhir.r5.model.Element) b;
if (lang != null) { if (locale != null) {
String v = ToolingExtensions.getLanguageTranslation(e, lang); String v = ToolingExtensions.getLanguageTranslation(e, locale.toString());
if (v != null) { if (v != null) {
return v; return v;
} }
// no? then see if the tx service can translate it for us // no? then see if the tx service can translate it for us
try { try {
ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(lang).withVersionFlexible(true), ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true),
codeSystem, null, e.primitiveValue(), null); codeSystem, null, e.primitiveValue(), null);
if (t.isOk() && t.getDisplay() != null) { if (t.isOk() && t.getDisplay() != null) {
return t.getDisplay(); return t.getDisplay();
@ -788,14 +844,14 @@ public class RenderingContext extends RenderingI18nContext {
} }
public String getTranslatedCode(Enumeration<?> e, String codeSystem) { public String getTranslatedCode(Enumeration<?> e, String codeSystem) {
if (lang != null) { if (locale != null) {
String v = ToolingExtensions.getLanguageTranslation(e, lang); String v = ToolingExtensions.getLanguageTranslation(e, locale.toString());
if (v != null) { if (v != null) {
return v; return v;
} }
// no? then see if the tx service can translate it for us // no? then see if the tx service can translate it for us
try { try {
ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(lang).withVersionFlexible(true), ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true),
codeSystem, null, e.getCode(), null); codeSystem, null, e.getCode(), null);
if (t.isOk() && t.getDisplay() != null) { if (t.isOk() && t.getDisplay() != null) {
return t.getDisplay(); return t.getDisplay();
@ -818,14 +874,14 @@ public class RenderingContext extends RenderingI18nContext {
} }
public String getTranslatedCode(Element e, String codeSystem) { public String getTranslatedCode(Element e, String codeSystem) {
if (lang != null) { if (locale != null) {
// first we look through the translation extensions // first we look through the translation extensions
for (Element ext : e.getChildrenByName("extension")) { for (Element ext : e.getChildrenByName("extension")) {
String url = ext.getNamedChildValue("url"); String url = ext.getNamedChildValue("url");
if (url.equals(ToolingExtensions.EXT_TRANSLATION)) { if (url.equals(ToolingExtensions.EXT_TRANSLATION)) {
Base e1 = ext.getExtensionValue("lang"); Base e1 = ext.getExtensionValue("lang");
if (e1 != null && e1.primitiveValue() != null && e1.primitiveValue().equals(lang)) { if (e1 != null && e1.primitiveValue() != null && e1.primitiveValue().equals(locale.toString())) {
e1 = ext.getExtensionValue("content"); e1 = ext.getExtensionValue("content");
if (e1 != null && e1.isPrimitive()) { if (e1 != null && e1.isPrimitive()) {
return e1.primitiveValue(); return e1.primitiveValue();
@ -835,7 +891,7 @@ public class RenderingContext extends RenderingI18nContext {
} }
// no? then see if the tx service can translate it for us // no? then see if the tx service can translate it for us
try { try {
ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(lang).withVersionFlexible(true), ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true),
codeSystem, null, e.primitiveValue(), null); codeSystem, null, e.primitiveValue(), null);
if (t.isOk() && t.getDisplay() != null) { if (t.isOk() && t.getDisplay() != null) {
return t.getDisplay(); return t.getDisplay();

View File

@ -776,6 +776,15 @@ public class ToolingExtensions {
// throw new Error("Attempt to assign multiple OIDs to value set "+vs.getName()+" ("+vs.getUrl()+"). Has "+readStringExtension(vs, EXT_OID)+", trying to add "+oid); // throw new Error("Attempt to assign multiple OIDs to value set "+vs.getName()+" ("+vs.getUrl()+"). Has "+readStringExtension(vs, EXT_OID)+", trying to add "+oid);
// } // }
public static boolean hasLanguageTranslations(Element element) {
for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) {
return true;
}
}
return false;
}
public static boolean hasLanguageTranslation(Element element, String lang) { public static boolean hasLanguageTranslation(Element element, String lang) {
for (Extension e : element.getExtension()) { for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) { if (e.getUrl().equals(EXT_TRANSLATION)) {
@ -802,6 +811,20 @@ public class ToolingExtensions {
return null; return null;
} }
public static StringType getLanguageTranslationElement(Element element, String lang) {
for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) {
Extension e1 = ExtensionHelper.getExtension(e, "lang");
if (e1 != null && e1.getValue() != null && e1.getValue() instanceof CodeType && ((CodeType) e1.getValue()).getValue().equals(lang)) {
e1 = ExtensionHelper.getExtension(e, "content");
return ((StringType) e1.getValue());
}
}
}
return null;
}
public static void addLanguageTranslation(Element element, String lang, String value) { public static void addLanguageTranslation(Element element, String lang, String value) {
if (Utilities.noString(lang) || Utilities.noString(value)) if (Utilities.noString(lang) || Utilities.noString(value))
return; return;

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.utilities;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.time.FastDateFormat;
@ -42,4 +43,25 @@ public class DateTimeUtil {
return ourHumanDateTimeFormat.format(theValue); return ourHumanDateTimeFormat.format(theValue);
} }
} }
public static String toHumanDisplay(Locale locale, TimeZone theTimeZone, TemporalPrecisionEnum thePrecision, Date theValue) {
Calendar value = theTimeZone != null ? Calendar.getInstance(theTimeZone) : Calendar.getInstance();
value.setTime(theValue);
FastDateFormat dateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM, locale);
FastDateFormat dateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM, locale);
switch (thePrecision) {
case YEAR:
case MONTH:
case DAY:
return dateFormat.format(value);
case MILLI:
case SECOND:
default:
return dateTimeFormat.format(value);
}
}
} }

View File

@ -29,7 +29,7 @@ public abstract class I18nBase {
if (Objects.nonNull(locale)) { if (Objects.nonNull(locale)) {
return locale; return locale;
} else { } else {
return Locale.US; return Locale.getDefault();
} }
} }