[i18b sprint] 3760 translations loading (#341)

* renamed I18nBundle

* added I18nBundle interface

* Added translation provider

* prototype of TranslationConverter

* convert all properties

* fixes

* added caching

* Removed obsolete code

* Improved logging

* fixed getting already existing label

* Fix to get RDF Service for configuration models

* fix translation request query

* added INTERFACE_I18N_FIRSTTIME_BACKUP model

* converter test added

* formatting fixes

* Translation provider tests added

* cleanups, added cache test for translation provider

* fix: get theme info from web app dao factory as sparql queries on both content and configuration models not supported
This commit is contained in:
Georgy Litvinov 2022-11-25 15:25:58 +01:00 committed by GitHub
parent 8ddbc8fc00
commit 9e3a3f7451
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1041 additions and 718 deletions

View file

@ -1,93 +0,0 @@
package edu.cornell.mannlib.vitro.webapp.i18n;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
import stubs.javax.servlet.ServletContextStub;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale;
/* $This file is distributed under the terms of the license in LICENSE$ */
/**
* Test the I18N functionality.
*
* Start by checking the logic that finds approximate matches for
* language-specific property files.
*/
public class I18nTest extends AbstractTestClass {
private static final List<Locale> SELECTABLE_LOCALES = locales("es_MX",
"en_US");
ServletContextStub ctx;
@Before
public void setup() {
ctx = new ServletContextStub();
}
@Test
public void noMatchOnLanguageRegion() {
assertLocales("fr_CA", SELECTABLE_LOCALES, "fr_CA", "fr", "");
}
@Test
public void noMatchOnLanguage() {
assertLocales("fr", SELECTABLE_LOCALES, "fr", "");
}
@Test
public void noMatchOnRoot() {
assertLocales("", SELECTABLE_LOCALES, "");
}
@Test
public void matchOnLanguageRegion() {
assertLocales("es_ES", SELECTABLE_LOCALES, "es_ES", "es", "es_MX", "");
}
@Test
public void matchOnLanguage() {
assertLocales("es", SELECTABLE_LOCALES, "es", "es_MX", "");
}
// ----------------------------------------------------------------------
// Helper methods
// ----------------------------------------------------------------------
private void assertLocales(String requested, List<Locale> selectable,
String... expected) {
SelectedLocale.setSelectableLocales(ctx, selectable);
List<Locale> expectedLocales = locales(expected);
I18n.ThemeBasedControl control = new I18n.ThemeBasedControl(ctx,
"bogusThemeDirectory");
List<Locale> actualLocales = control.getCandidateLocales(
"bogusBaseName", locale(requested));
assertEquals("Expected locales", expectedLocales, actualLocales);
}
private static List<Locale> locales(String... strings) {
List<Locale> locales = new ArrayList<>();
for (String s : strings) {
locales.add(locale(s));
}
return locales;
}
private static Locale locale(String s) {
String[] parts = s.split("_");
String language = (parts.length > 0) ? parts[0] : "";
String country = (parts.length > 1) ? parts[1] : "";
String variant = (parts.length > 2) ? parts[2] : "";
return new Locale(language, country, variant);
}
}

View file

@ -0,0 +1,92 @@
package edu.cornell.mannlib.vitro.webapp.i18n;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.ontology.OntModel;
import org.apache.jena.rdf.model.Selector;
import org.apache.jena.rdf.model.SimpleSelector;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.rdf.model.impl.PropertyImpl;
import org.junit.Test;
import stubs.javax.servlet.ServletContextStub;
public class TranslationConverterTest {
private static final String WILMA = "wilma";
private static final String HAS_THEME = "http://vivoweb.org/ontology/core/properties/vocabulary#hasTheme";
private static final String VITRO = "Vitro";
private static final String VIVO = "VIVO";
private static final String HAS_APP = "http://vivoweb.org/ontology/core/properties/vocabulary#hasApp";
private static final String HAS_KEY = "http://vivoweb.org/ontology/core/properties/vocabulary#hasKey";
private static final String ROOT_PATH = "src/test/resources/edu/cornell/mannlib/vitro/webapp/i18n/TranslationConverterTest/root";
private static final String INIT_N3_FILE = "src/test/resources/edu/cornell/mannlib/vitro/webapp/i18n/TranslationConverterTest/modelInitContent.n3";
ServletContextStub ctx = new ServletContextStub();
private OntModel model;
@Test
public void testConversion() throws FileNotFoundException {
VitroResourceBundle.addAppPrefix("customprefix");
VitroResourceBundle.addAppPrefix("vivo");
TranslationConverter converter = TranslationConverter.getInstance();
model = converter.memModel;
File n3 = new File(INIT_N3_FILE);
assertTrue(model.isEmpty());
model.read(new FileReader(n3), null, "n3");
assertFalse(model.isEmpty());
File appI18n = new File(ROOT_PATH + TranslationConverter.APP_I18N_PATH);
File localI18n = new File(ROOT_PATH + TranslationConverter.LOCAL_I18N_PATH);
File themes = new File(ROOT_PATH + TranslationConverter.THEMES_PATH);
ctx.setRealPath(TranslationConverter.APP_I18N_PATH, appI18n.getAbsolutePath());
ctx.setRealPath(TranslationConverter.LOCAL_I18N_PATH, localI18n.getAbsolutePath());
ctx.setRealPath(TranslationConverter.THEMES_PATH, themes.getAbsolutePath());
converter.ctx = ctx;
converter.convertAll();
assertEquals(2, getCount(HAS_KEY, "test_key_all_en_US"));
assertEquals(2, getCount(HAS_KEY, "test_key_all_en_CA"));
assertEquals(2, getCount(HAS_KEY, "test_key_all"));
assertEquals(1, getCount(HAS_KEY, "property_to_overwrite"));
assertTrue(n3TranslationValueIsOverwrittenByProperty(model));
assertEquals(3, getCount(HAS_THEME, WILMA));
assertEquals(6, getCount(HAS_APP, VITRO));
assertEquals(3, getCount(HAS_APP, VIVO));
// printResultModel();
}
private void printResultModel() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
model.write(baos, "N3");
System.out.println(baos.toString());
}
private int getCount(String hasTheme, String wilma) {
Selector selector = new SimpleSelector(null, new PropertyImpl(hasTheme), wilma);
StmtIterator it = model.listStatements(selector);
int count = 0;
while (it.hasNext()) {
count++;
it.next();
}
return count;
}
private boolean n3TranslationValueIsOverwrittenByProperty(OntModel model) {
return model.getGraph().contains(
NodeFactory.createURI("urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663600"),
NodeFactory.createURI("http://www.w3.org/2000/01/rdf-schema#label"),
NodeFactory.createLiteral("value from properties file","en-US"));
}
}

View file

@ -0,0 +1,161 @@
package edu.cornell.mannlib.vitro.webapp.i18n;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Collections;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.junit.Test;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel;
public class TranslationProviderTest {
private static final String VITRO = "Vitro";
private static final String VIVO = "VIVO";
private static final String ROOT = "src/test/resources/edu/cornell/mannlib/vitro/webapp/i18n/TranslationProviderTest/";
private static final String TRANSLATIONS_N3_FILE = ROOT + "modelInitContent.n3";
private static final String WILMA = "wilma";
private static final String NEMO = "nemo";
private Model i18nModel;
private RDFServiceModel rdfService;
private TranslationProvider tp;
public void init(String i18nFile, String themeName, String appName) throws FileNotFoundException {
i18nModel = ModelFactory.createDefaultModel();
i18nModel.read(new FileReader(new File(i18nFile)), null, "n3");
Dataset ds = DatasetFactory.createTxnMem();
ds.addNamedModel("http://vitro.mannlib.cornell.edu/default/interface-i18n", i18nModel);
rdfService = new RDFServiceModel(ds);
tp = TranslationProvider.getInstance();
tp.rdfService = rdfService;
tp.setTheme(themeName);
tp.application = appName;
tp.clearCache();
}
@Test
public void testNotExistingKey() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VITRO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "non_existing_key", array);
assertEquals("ERROR: Translation not found 'non_existing_key'", translation);
}
@Test
public void testVitroWilmaEnUS() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VITRO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey Vitro wilma en-US", translation);
}
@Test
public void testVitroWilmaDeDE() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VITRO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("de-DE"), "testkey", array);
assertEquals("testkey Vitro wilma de-DE", translation);
}
@Test
public void testVIVOWilmaEnUS() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey VIVO wilma en-US", translation);
}
@Test
public void testVIVOWilmaDeDE() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("de-DE"), "testkey", array);
assertEquals("testkey VIVO wilma de-DE", translation);
}
@Test
public void testThemeFallbackVitroNemoEnUS() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, NEMO, VITRO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey Vitro no theme en-US", translation);
}
@Test
public void testThemeFallbackVitroNemoDeDE() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, NEMO, VITRO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("de-DE"), "testkey", array);
assertEquals("testkey Vitro no theme de-DE", translation);
}
@Test
public void testThemeFallbackVIVONemoEnUS() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, NEMO, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey VIVO no theme en-US", translation);
}
@Test
public void testThemeFallbackVIVONemoDeDE() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, NEMO, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("de-DE"), "testkey", array);
assertEquals("testkey VIVO no theme de-DE", translation);
}
@Test
public void testAppFallbackVIVONemoEnUS() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey_app_fallback", array);
assertEquals("testkey_app_fallback Vitro wilma en-US", translation);
}
@Test
public void testAppFallbackVIVONemoDeDE() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("de-DE"), "testkey_app_fallback", array);
assertEquals("testkey_app_fallback Vitro wilma de-DE", translation);
}
@Test
public void testAppAndThemeFallbackVIVONemoEnUS() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, NEMO, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey_app_fallback", array);
assertEquals("testkey_app_fallback Vitro no theme en-US", translation);
}
@Test
public void testAppAndThemeFallbackVIVONemoDeDE() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, NEMO, VIVO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("de-DE"), "testkey_app_fallback", array);
assertEquals("testkey_app_fallback Vitro no theme de-DE", translation);
}
@Test
public void testCache() throws FileNotFoundException {
init(TRANSLATIONS_N3_FILE, WILMA, VITRO);
Object array[] = {};
String translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey Vitro wilma en-US", translation);
tp.application = VIVO;
translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey Vitro wilma en-US", translation);
tp.clearCache();
translation = tp.getTranslation(Collections.singletonList("en-US"), "testkey", array);
assertEquals("testkey VIVO wilma en-US", translation);
}
}

View file

@ -51,14 +51,11 @@ public class I18nStub extends I18n {
}
@Override
protected I18nBundle getBundle(String bundleName, HttpServletRequest req) {
return new I18nBundleStub(bundleName);
protected I18nBundle getBundle( HttpServletRequest req) {
return new I18nBundleStub();
}
private class I18nBundleStub extends I18nBundle {
public I18nBundleStub(String bundleName) {
super(bundleName, new DummyResourceBundle(), null);
}
private class I18nBundleStub implements I18nBundle {
@Override
public String text(String key, Object... parameters) {

View file

@ -0,0 +1,12 @@
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663600>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "value from n3 file"@en-US ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"VIVO" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"property_to_overwrite" .

View file

@ -0,0 +1 @@
test_key_all_en_CA = test value all_en_CA

View file

@ -0,0 +1,2 @@
test_key = test value vivo_all_en_US
property_to_overwrite = value from properties file

View file

@ -0,0 +1 @@
test_key_all_en_US = test value customprefix_all_en_US

View file

@ -0,0 +1 @@
test_key_all_en_CA = test value all_en_CA wilma

View file

@ -0,0 +1 @@
test_key_all_en_US = test value vivo_all_en_US wilma

View file

@ -0,0 +1,77 @@
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663600>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey VIVO no theme en-US"@en-US ;
rdfs:label "testkey VIVO no theme de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"VIVO" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey" .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663601>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey Vitro no theme en-US"@en-US ;
rdfs:label "testkey Vitro no theme de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"Vitro" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey" .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663602>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey VIVO wilma en-US"@en-US ;
rdfs:label "testkey VIVO wilma de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"VIVO" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasTheme>
"wilma" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey" .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663603>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey Vitro wilma en-US"@en-US ;
rdfs:label "testkey Vitro wilma de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"Vitro" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasTheme>
"wilma" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey" .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663604>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey Vitro vitro en-US"@en-US ;
rdfs:label "testkey Vitro vitro de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"Vitro" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasTheme>
"vitro" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey" .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663605>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey_app_fallback Vitro wilma en-US"@en-US ;
rdfs:label "testkey_app_fallback Vitro wilma de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"Vitro" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasTheme>
"wilma" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey_app_fallback" .
<urn:uuid:8c80dbf5-adda-41d5-a6fe-d5efde663606>
a owl:NamedIndividual , <http://vivoweb.org/ontology/core/properties/vocabulary#PropertyKey> ;
rdfs:label "testkey_app_fallback Vitro no theme en-US"@en-US ;
rdfs:label "testkey_app_fallback Vitro no theme de-DE"@de-DE ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasApp>
"Vitro" ;
<http://vivoweb.org/ontology/core/properties/vocabulary#hasKey>
"testkey_app_fallback" .