mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2025-12-30 18:12:53 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8da621558 | ||
|
|
6f12fbda94 | ||
|
|
cf83692e71 | ||
|
|
27417e80e1 | ||
|
|
bab675b806 | ||
|
|
d3790adbd8 | ||
|
|
d8aa023b28 | ||
|
|
4217e237fb | ||
|
|
c7e2584472 | ||
|
|
e30e04384f | ||
|
|
6fb9ccb821 |
@@ -1,3 +1,11 @@
|
||||
# [0.17.0](https://gitlab.com/openstapps/core/compare/v0.16.0...v0.17.0) (2019-04-16)
|
||||
|
||||
|
||||
|
||||
# [0.16.0](https://gitlab.com/openstapps/core/compare/v0.15.0...v0.16.0) (2019-04-15)
|
||||
|
||||
|
||||
|
||||
# [0.15.0](https://gitlab.com/openstapps/core/compare/v0.14.0...v0.15.0) (2019-04-09)
|
||||
|
||||
|
||||
|
||||
1633
package-lock.json
generated
1633
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openstapps/core",
|
||||
"version": "0.16.0",
|
||||
"version": "0.18.0",
|
||||
"description": "StAppsCore - Generalized model of data",
|
||||
"keywords": [
|
||||
"Model",
|
||||
@@ -14,7 +14,7 @@
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "npm run tslint && npm run compile && npm run pack && npm run schema && npm run documentation",
|
||||
"build": "npm run tslint && npm run compile && npm run pack && npm run schema",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
|
||||
"check-configuration": "openstapps-configuration",
|
||||
"compile": "rimraf lib && tsc",
|
||||
@@ -28,33 +28,36 @@
|
||||
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
|
||||
"contributors": [
|
||||
"Anselm Stordeur <anselmstordeur@gmail.com>",
|
||||
"Jovan Krunic <jovan.krunic@gmail.com>",
|
||||
"Jovan Krunić <jovan.krunic@gmail.com>",
|
||||
"Andreas Lehmann",
|
||||
"Sebastian Lange",
|
||||
"Rainer Killinger",
|
||||
"Imran Hossain"
|
||||
"Imran Hossain",
|
||||
"Michel Jonathan Schmitz",
|
||||
"Wieland Schöbl"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/geojson": "1.0.6",
|
||||
"@types/json-patch": "0.0.30",
|
||||
"fast-clone": "1.5.13",
|
||||
"json-patch": "0.7.0",
|
||||
"jsonschema": "1.2.4",
|
||||
"ts-optchain": "0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openstapps/configuration": "0.11.0",
|
||||
"@openstapps/core-tools": "0.5.1",
|
||||
"@krlwlfrt/async-pool": "0.1.0",
|
||||
"@openstapps/configuration": "0.12.0",
|
||||
"@openstapps/core-tools": "0.6.0",
|
||||
"@openstapps/logger": "0.0.5",
|
||||
"@types/chai": "4.1.7",
|
||||
"@types/node": "10.14.4",
|
||||
"@types/rimraf": "2.0.2",
|
||||
"async-pool-native": "0.1.0",
|
||||
"chai": "4.2.0",
|
||||
"commander": "2.20.0",
|
||||
"conventional-changelog-cli": "2.0.12",
|
||||
"mocha": "6.1.3",
|
||||
"mocha": "6.1.4",
|
||||
"mocha-typescript": "1.1.17",
|
||||
"nyc": "13.3.0",
|
||||
"nyc": "14.0.0",
|
||||
"rimraf": "2.6.3",
|
||||
"ts-node": "8.1.0",
|
||||
"tslint": "5.15.0",
|
||||
|
||||
@@ -32,6 +32,7 @@ import {SCRoom, SCRoomMeta, SCRoomWithoutReferences} from './things/Room';
|
||||
import {SCSemester, SCSemesterMeta, SCSemesterWithoutReferences} from './things/Semester';
|
||||
import {SCSetting, SCSettingMeta, SCSettingWithoutReferences} from './things/Setting';
|
||||
import {SCSportCourse, SCSportCourseMeta, SCSportCourseWithoutReferences} from './things/SportCourse';
|
||||
import {SCStudyModule, SCStudyModuleMeta, SCStudyModuleWithoutReferences} from './things/StudyModule';
|
||||
import {SCTicket, SCTicketMeta, SCTicketWithoutReferences} from './things/Ticket';
|
||||
import {SCToDo, SCToDoMeta, SCToDoWithoutReferences} from './things/ToDo';
|
||||
import {SCTour, SCTourMeta, SCTourWithoutReferences} from './things/Tour';
|
||||
@@ -62,6 +63,7 @@ export const SCClasses: { [K in SCThingType]: any } = {
|
||||
'semester': SCSemesterMeta,
|
||||
'setting': SCSettingMeta,
|
||||
'sport course': SCSportCourseMeta,
|
||||
'study module': SCStudyModuleMeta,
|
||||
'ticket': SCTicketMeta,
|
||||
'todo': SCToDoMeta,
|
||||
'tour': SCTourMeta,
|
||||
@@ -87,6 +89,7 @@ export type SCThingsWithoutDiff =
|
||||
| SCSemester
|
||||
| SCSetting
|
||||
| SCSportCourse
|
||||
| SCStudyModule
|
||||
| SCTicket
|
||||
| SCToDo
|
||||
| SCTour
|
||||
@@ -127,11 +130,12 @@ export type SCAssociatedThingWithoutReferences<THING extends SCThings> =
|
||||
THING extends SCSemester ? SCSemesterWithoutReferences :
|
||||
THING extends SCSetting ? SCSettingWithoutReferences :
|
||||
THING extends SCSportCourse ? SCSportCourseWithoutReferences :
|
||||
THING extends SCTicket ? SCTicketWithoutReferences :
|
||||
THING extends SCToDo ? SCToDoWithoutReferences :
|
||||
THING extends SCTour ? SCTourWithoutReferences :
|
||||
THING extends SCVideo ? SCVideoWithoutReferences :
|
||||
never;
|
||||
THING extends SCStudyModule ? SCStudyModuleWithoutReferences :
|
||||
THING extends SCTicket ? SCTicketWithoutReferences :
|
||||
THING extends SCToDo ? SCToDoWithoutReferences :
|
||||
THING extends SCTour ? SCTourWithoutReferences :
|
||||
THING extends SCVideo ? SCVideoWithoutReferences :
|
||||
never;
|
||||
|
||||
/**
|
||||
* Thing for a thing without references
|
||||
@@ -156,8 +160,9 @@ export type SCAssociatedThing<THING extends SCThings> =
|
||||
THING extends SCSemesterWithoutReferences ? SCSemester :
|
||||
THING extends SCSettingWithoutReferences ? SCSetting :
|
||||
THING extends SCSportCourseWithoutReferences ? SCSportCourse :
|
||||
THING extends SCTicketWithoutReferences ? SCTicket :
|
||||
THING extends SCToDoWithoutReferences ? SCToDo :
|
||||
THING extends SCTourWithoutReferences ? SCTour :
|
||||
THING extends SCVideoWithoutReferences ? SCVideo :
|
||||
never;
|
||||
THING extends SCStudyModuleWithoutReferences ? SCStudyModule :
|
||||
THING extends SCTicketWithoutReferences ? SCTicket :
|
||||
THING extends SCToDoWithoutReferences ? SCToDo :
|
||||
THING extends SCTourWithoutReferences ? SCTour :
|
||||
THING extends SCVideoWithoutReferences ? SCVideo :
|
||||
never;
|
||||
|
||||
@@ -41,6 +41,7 @@ export enum SCThingType {
|
||||
Semester = 'semester',
|
||||
Setting = 'setting',
|
||||
SportCourse = 'sport course',
|
||||
StudyModule = 'study module',
|
||||
Ticket = 'ticket',
|
||||
ToDo = 'todo',
|
||||
Tour = 'tour',
|
||||
|
||||
@@ -12,30 +12,33 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCClasses, SCThingsField} from './Classes';
|
||||
import {SCClasses} from './Classes';
|
||||
import {SCThing, SCThingType} from './Thing';
|
||||
|
||||
import {isThing} from './types/Guards';
|
||||
import {SCTranslations} from './types/i18n';
|
||||
|
||||
import clone = require('fast-clone');
|
||||
import {Defined, OCType} from 'ts-optchain';
|
||||
|
||||
/**
|
||||
* SCThingTranslator class
|
||||
*/
|
||||
export class SCThingTranslator {
|
||||
/**
|
||||
* Property representing the translators base language.
|
||||
* This means every translation is given for this language.
|
||||
*/
|
||||
private baseLanguage: keyof SCTranslations<SCThing>;
|
||||
|
||||
/**
|
||||
* Property representing the translators target language
|
||||
*/
|
||||
private language: keyof SCTranslations<SCThing>;
|
||||
private _language: keyof SCTranslations<SCThing>;
|
||||
|
||||
/**
|
||||
* Property provinding a mapping from a SCThingType to its known own meta class.
|
||||
* Property representing the translators base language
|
||||
* This means every translation is given for this language
|
||||
*/
|
||||
private cache: LRUCache<SCThing>;
|
||||
|
||||
/**
|
||||
* Property providing a mapping from a SCThingType to its known own meta class
|
||||
*/
|
||||
private metaClasses: typeof SCClasses;
|
||||
|
||||
@@ -45,42 +48,44 @@ export class SCThingTranslator {
|
||||
* // returns translator instance for german
|
||||
* new SCThingTranslator('de');
|
||||
*/
|
||||
constructor(language: keyof SCTranslations<SCThing>, baseLanguage?: keyof SCTranslations<SCThing>) {
|
||||
this.baseLanguage = baseLanguage ? baseLanguage : 'en';
|
||||
this.language = language;
|
||||
constructor(language: keyof SCTranslations<SCThing>, cacheCapacity: number = 200) {
|
||||
this.cache = new LRUCache(cacheCapacity);
|
||||
this._language = language;
|
||||
this.metaClasses = SCClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for language property
|
||||
*/
|
||||
get language(): keyof SCTranslations<SCThing> {
|
||||
return this._language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for language property. Also flushes translation cache
|
||||
*
|
||||
* @param language The language the translator instance will use from now on
|
||||
*/
|
||||
set language(language: keyof SCTranslations<SCThing>) {
|
||||
if (language !== this._language) {
|
||||
this.cache.flush();
|
||||
}
|
||||
this._language = language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field value translation recursively
|
||||
*
|
||||
* @param firstObject Top level object that gets passed through the recursion
|
||||
* @param data The intermediate object / primitive returned by the Proxys get() method
|
||||
* @param keyPath The keypath that (in the end) leads to the translatable property (when added to firstObject)
|
||||
* @returns an OCType<T> object allowing for access to translations or a translated value(s)
|
||||
*/
|
||||
private deeptranslate<T, K extends SCThing>(firstObject: K, data?: T, keyPath?: string): OCType<T> {
|
||||
private deeptranslate<T>(data?: T): OCType<T> {
|
||||
const proxy = new Proxy(
|
||||
((defaultValue?: Defined<T>) => (data == null ? defaultValue : data)) as OCType<T>,
|
||||
{
|
||||
get: (target, key) => {
|
||||
const obj: any = target();
|
||||
const extendedKeyPath = [keyPath, key.toString()].filter((e) => e != null).join('.');
|
||||
let possiblePrimitive = obj[key];
|
||||
// check if obj[key] is an array that contains primitive type (arrays in SCThings are not mixing types)
|
||||
if (obj[key] instanceof Array && obj[key].length) {
|
||||
possiblePrimitive = obj[key][0];
|
||||
}
|
||||
if (typeof possiblePrimitive === 'string' ||
|
||||
typeof possiblePrimitive === 'number' ||
|
||||
typeof possiblePrimitive === 'boolean') {
|
||||
// returns final translation for primitive data types
|
||||
return this.deeptranslate(firstObject,
|
||||
this.getFieldValueTranslation(firstObject, extendedKeyPath),
|
||||
extendedKeyPath);
|
||||
}
|
||||
// recursion to get more calls to the Proxy handler 'get()' (key path not complete)
|
||||
return this.deeptranslate(firstObject, obj[key], extendedKeyPath);
|
||||
return this.deeptranslate(obj[key]);
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -98,19 +103,19 @@ export class SCThingTranslator {
|
||||
language: keyof SCTranslations<T>): object | undefined {
|
||||
const fieldTranslations = {};
|
||||
const metaClass = this.getMetaClassInstance(thingType);
|
||||
if (metaClass === undefined) {
|
||||
if (typeof metaClass === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Assigns every property in fieldTranslations to the known base language translation
|
||||
if (metaClass.fieldTranslations[this.baseLanguage] !== undefined) {
|
||||
Object.keys(metaClass.fieldTranslations[this.baseLanguage]).forEach((key) => {
|
||||
(fieldTranslations as any)[key] = metaClass.fieldTranslations[this.baseLanguage][key];
|
||||
if (typeof metaClass.fieldTranslations.en !== 'undefined') {
|
||||
Object.keys(metaClass.fieldTranslations.en).forEach((key) => {
|
||||
(fieldTranslations as any)[key] = metaClass.fieldTranslations.en[key];
|
||||
});
|
||||
}
|
||||
|
||||
// Assigns every property in fieldTranslations to the known translation in given language
|
||||
if (metaClass.fieldTranslations[language] !== undefined) {
|
||||
if (typeof metaClass.fieldTranslations[language] !== 'undefined') {
|
||||
Object.keys(metaClass.fieldTranslations[language]).forEach((key) => {
|
||||
(fieldTranslations as any)[key] = metaClass.fieldTranslations[language][key];
|
||||
});
|
||||
@@ -132,120 +137,34 @@ export class SCThingTranslator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns property value at a certain (key) path of an object.
|
||||
* @example
|
||||
* // returns value of dish.offers[0].inPlace.categories[1]
|
||||
* const dish: SCDish = {...};
|
||||
* this.valueFromPath(dish, 'offers[0].inPlace.categories[1]');
|
||||
* @param path Key path to evaluate
|
||||
* @param obj Object to evaluate the key path upon
|
||||
* @param separator Key path seperation element. Defaults to '.'
|
||||
* @returns Property value at at key path
|
||||
* Applies known field value translations of the given SCThings meta class to an instance
|
||||
* Translated values overwrite current values inplace (destructive)
|
||||
*
|
||||
* @param language The language the thing is translated to
|
||||
* @param thing The thing that will be translated
|
||||
* @returns The thing with translated meta field values
|
||||
*/
|
||||
private valueFromPath<T extends SCThing>(path: string, obj: T, separator = '.') {
|
||||
path = path.replace(/\[/g, '.');
|
||||
path = path.replace(/\]/g, '.');
|
||||
path = path.replace(/\.\./g, '.');
|
||||
path = path.replace(/\.$/, '');
|
||||
const properties = path.split(separator);
|
||||
return properties.reduce((prev: any, curr: any) => prev && prev[curr], obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get field value translation
|
||||
* @example
|
||||
* // returns translation of the property (if available) in the language defined when creating the translator object
|
||||
* const dish: SCDish = {...};
|
||||
* translator.translate(dish, 'offers[0].inPlace.categories[1]');
|
||||
* @param thing SCThing to get value translation for
|
||||
* @param field Field to get value translation for (keypath allowed)
|
||||
* @returns Translated value(s) or value(s) itself
|
||||
*/
|
||||
public getFieldValueTranslation<T extends SCThing>(thing: T,
|
||||
field: SCThingsField): string | string[] {
|
||||
let translationPath = 'translations.' + this.language + '.' + field;
|
||||
const regexTrimProperties = /.*(?:(\..*)(\[\d+\])|(\.[^\d]*$)|(\..*)(\.[\d]*$))/;
|
||||
|
||||
const pathMatch = field.match(regexTrimProperties);
|
||||
|
||||
// when translation is given in thing
|
||||
let translation = this.valueFromPath(translationPath, thing);
|
||||
if (translation) {
|
||||
return translation;
|
||||
} else if (pathMatch && pathMatch[1] && pathMatch[2] || pathMatch && pathMatch[4] && pathMatch[5]) {
|
||||
// accessing iteratable of nested thing
|
||||
const keyPath = (pathMatch[1] ? pathMatch[1] : pathMatch[4]) + (pathMatch[2] ? pathMatch[2] : pathMatch[5]);
|
||||
const redactedField = field.replace(keyPath, '');
|
||||
|
||||
// when translation is given in nested thing
|
||||
translationPath = `${redactedField}.translations.${this.language}${keyPath}`;
|
||||
translation = this.valueFromPath(translationPath, thing);
|
||||
if (translation) {
|
||||
return translation;
|
||||
}
|
||||
|
||||
// when translation is given in nested meta thing via iterateable index
|
||||
const nestedType = this.valueFromPath(field.replace(keyPath, '.type'), thing) as SCThingType;
|
||||
translationPath = `fieldValueTranslations.${this.language}${keyPath}`;
|
||||
translation = this.valueFromPath(translationPath.replace(
|
||||
/\[(?=[^\[]*$).*|(?=[\d+]*$).*/, '[' + this.valueFromPath(field, thing) + ']'),
|
||||
this.getMetaClassInstance(nestedType));
|
||||
if (translation) {
|
||||
return translation;
|
||||
}
|
||||
|
||||
} else if (pathMatch && pathMatch[3]) {
|
||||
// accessing meta or instance of nested thing primitive value depth > 0
|
||||
const keyPath = pathMatch[3];
|
||||
const redactedField = field.replace(pathMatch[3], '');
|
||||
|
||||
// when translation is given in nested thing
|
||||
translationPath = `${redactedField}.translations.${this.language}${keyPath}`;
|
||||
if (this.valueFromPath(translationPath, thing)) {
|
||||
return this.valueFromPath(translationPath, thing);
|
||||
}
|
||||
|
||||
// when translation is given in nested meta thing
|
||||
const nestedType = this.valueFromPath(field.replace(keyPath, '.type'), thing) as SCThingType;
|
||||
translationPath = `fieldValueTranslations.${this.language}${keyPath}`;
|
||||
translation = this.valueFromPath(translationPath, this.getMetaClassInstance(nestedType));
|
||||
if (translation instanceof Object) { // lookup translated keys in meta thing property
|
||||
const translations: string[] = [];
|
||||
this.valueFromPath(field, thing).forEach((key: string) => {
|
||||
translationPath = `fieldValueTranslations.${this.language}${keyPath}.${key}`;
|
||||
translations.push(this.valueFromPath(translationPath, this.getMetaClassInstance(nestedType)));
|
||||
});
|
||||
return translations;
|
||||
}
|
||||
if (!translation) { // translation not given, return as is
|
||||
return this.valueFromPath(field, thing) as string;
|
||||
}
|
||||
return translation;
|
||||
private replaceAvailableMetaFieldValueTranslations(instance: any,
|
||||
language: keyof SCTranslations<any>): any {
|
||||
const metaClass = this.getMetaClassInstance(instance.type);
|
||||
if (typeof metaClass === 'undefined') {
|
||||
return instance;
|
||||
}
|
||||
// accessing meta thing primitive value depth = 0
|
||||
translationPath = `fieldValueTranslations.${this.language}.${field}`;
|
||||
translation = this.valueFromPath(translationPath, this.getMetaClassInstance(thing.type));
|
||||
if (translation) {
|
||||
if (translation instanceof Object) { // lookup translated keys in meta thing property
|
||||
const translations: string[] = [];
|
||||
this.valueFromPath(field, thing).forEach((key: string) => {
|
||||
translationPath = `fieldValueTranslations.${this.language}.${field}.${key}`;
|
||||
translations.push(this.valueFromPath(translationPath, this.getMetaClassInstance(thing.type)));
|
||||
});
|
||||
return translations;
|
||||
}
|
||||
return translation;
|
||||
if (typeof metaClass.fieldValueTranslations[language] !== 'undefined') {
|
||||
Object.keys(metaClass.fieldValueTranslations[language]).forEach((key) => {
|
||||
if (metaClass.fieldValueTranslations[language][key] instanceof Object) {
|
||||
// Assigns known translations of subproperties to property in given language (e.g. categories)
|
||||
Object.keys((instance as any)[key]).forEach((subKey) => {
|
||||
(instance as any)[key][subKey] =
|
||||
metaClass.fieldValueTranslations[language][key][(instance as any)[key][subKey]];
|
||||
});
|
||||
} else {
|
||||
// Assigns property to known translation of fieldValueTranslations in given language
|
||||
(instance as any)[key] = metaClass.fieldValueTranslations[language][key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// accessing meta thing primitive via iteratable index value depth = 0
|
||||
translation = this.valueFromPath(translationPath.replace(
|
||||
/\[(?=[^\[]*$).*|(?=[\d+]*$).*/, '[' + this.valueFromPath(field, thing) + ']'),
|
||||
this.getMetaClassInstance(thing.type));
|
||||
if (translation) {
|
||||
return translation;
|
||||
}
|
||||
// last resort: return as is
|
||||
return this.valueFromPath(field, thing) as string;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,33 +180,27 @@ export class SCThingTranslator {
|
||||
* @param data Top level object that gets passed through the recursion
|
||||
* @returns an OCType<T> object allowing for access to translations or a translated value(s)
|
||||
*/
|
||||
public translate<T extends SCThing>(data?: T): OCType<T> {
|
||||
public translate<T extends SCThing>(data: T): OCType<T> {
|
||||
return new Proxy(
|
||||
((defaultValue?: Defined<T>) => (data == null ? defaultValue : data)) as OCType<T>,
|
||||
{
|
||||
get: (target, key) => {
|
||||
const obj: any = target();
|
||||
let translatable = obj[key];
|
||||
if (obj[key] instanceof Array && obj[key].length) {
|
||||
translatable = obj[key][0];
|
||||
if (typeof obj[key][0] === 'object' && !obj[key][0].origin) {
|
||||
translatable = obj[key][0][Object.keys(obj[key][0])[0]];
|
||||
}
|
||||
const objTranslatedFromCache = this.cache.get(data);
|
||||
if (typeof objTranslatedFromCache !== 'undefined') {
|
||||
return this.deeptranslate((objTranslatedFromCache as any)[key]);
|
||||
}
|
||||
if (typeof translatable === 'string') {
|
||||
// retrieve final translation
|
||||
return this.deeptranslate(data!, this.getFieldValueTranslation(data!, key.toString()), key.toString());
|
||||
}
|
||||
// recursion to get more calls to the Proxy handler 'get()' (key path not complete)
|
||||
return this.deeptranslate(data!, obj[key], key.toString());
|
||||
const objTranslated = this.translateWholeThingDestructively(clone(obj));
|
||||
this.cache.putObject(objTranslated);
|
||||
return this.deeptranslate(objTranslated[key]);
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a SCThingType this function returns an object with the same basic structure as the corresponding SCThing.
|
||||
* All the values will be set to the known translations of the property/key name.
|
||||
* Given a SCThingType this function returns an object with the same basic structure as the corresponding SCThing
|
||||
* All the values will be set to the known translations of the property/key name
|
||||
* @example
|
||||
* const translatedMetaDish = translator.translatedPropertyNames<SCCourseOfStudies>(SCThingType.CourseOfStudies);
|
||||
* @param language The language the object is translated to
|
||||
@@ -300,4 +213,129 @@ export class SCThingTranslator {
|
||||
// return {...{}, ...this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T};
|
||||
return this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively translates the given object in-place
|
||||
* Translated values overwrite current values (destructive)
|
||||
*
|
||||
* @param language The language the thing is translated to
|
||||
* @param thing The thing that will be translated
|
||||
* @returns The thing translated
|
||||
*/
|
||||
public translateWholeThingDestructively(instance: any,
|
||||
language?: keyof SCTranslations<any>): any {
|
||||
const targetLanguage = (language) ? language : this.language;
|
||||
// Recursively call this function on all nested SCThings, arrays and objects
|
||||
Object.keys(instance).forEach((key) => {
|
||||
if (
|
||||
isThing((instance as any)[key]) ||
|
||||
instance[key] instanceof Array ||
|
||||
instance[key] instanceof Object) {
|
||||
instance[key] = this.translateWholeThingDestructively(instance[key], targetLanguage);
|
||||
}
|
||||
});
|
||||
|
||||
// Spread variable translations given by the connector into thing
|
||||
if (typeof instance.translations !== 'undefined') {
|
||||
if (typeof instance.translations![targetLanguage] !== 'undefined') {
|
||||
instance = {...instance, ...instance.translations![targetLanguage]} as typeof instance;
|
||||
}
|
||||
}
|
||||
// Spread known translations from meta classes into (partly) translated thing
|
||||
this.replaceAvailableMetaFieldValueTranslations(instance, targetLanguage);
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* LRUCache class
|
||||
* Small last recently used cache intended to get used by SCThingTranslator
|
||||
*/
|
||||
class LRUCache<T> {
|
||||
/**
|
||||
* Map property that manages cached content
|
||||
*/
|
||||
private entries: Map<string, T> = new Map<string, T>();
|
||||
|
||||
/**
|
||||
* Property representing cache maximum capacity
|
||||
*/
|
||||
private maxEntries: number;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @example
|
||||
* // returns LRUCache instance with a maximum capacity of 500
|
||||
* new LRUCache(500);
|
||||
*/
|
||||
constructor(maxEntries: number) {
|
||||
this.maxEntries = maxEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes cache / removes all entries
|
||||
*/
|
||||
public flush() {
|
||||
this.entries.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content from cache by key
|
||||
*/
|
||||
public get(somethingOrKey: string): T | undefined;
|
||||
|
||||
/**
|
||||
* Get content from cache by another objects uid
|
||||
*/
|
||||
public get<U extends SCThing>(something: U): T | undefined;
|
||||
|
||||
/**
|
||||
* Get content from cache by key or by another objects uid
|
||||
*
|
||||
* @param somethingOrKey The key which maps to the cached content or an object for which content has been cached
|
||||
* @returns If available the content connected to the key or somethingOrKey.uid property
|
||||
*/
|
||||
public get<U extends SCThing>(somethingOrKey: string | U): T | undefined {
|
||||
let key: string;
|
||||
if (typeof somethingOrKey === 'string') {
|
||||
key = somethingOrKey;
|
||||
} else if (isThing(somethingOrKey)) {
|
||||
key = somethingOrKey.uid;
|
||||
} else {
|
||||
throw new Error(`Passed argument ${somethingOrKey} cannot be key in LRUCache`);
|
||||
}
|
||||
|
||||
const entry = this.entries.get(key);
|
||||
if (entry) {
|
||||
// LRU behavior
|
||||
this.entries.delete(key);
|
||||
this.entries.set(key, entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Place content in cache by key
|
||||
*
|
||||
* @param key The key for which content should be cached
|
||||
* @param content The content that should be cached
|
||||
*/
|
||||
public put(key: string, content: T) {
|
||||
if (this.entries.size >= this.maxEntries) {
|
||||
// LRU behavior
|
||||
const keyToDelete = this.entries.keys().next().value;
|
||||
this.entries.delete(keyToDelete);
|
||||
}
|
||||
this.entries.set(key, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Place content in cache by another objects uid
|
||||
*
|
||||
* @param something The object that should be cached under something.uid
|
||||
*/
|
||||
public putObject<U extends SCThing>(something: U) {
|
||||
this.put(something.uid, (something as any) as T);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,14 @@ export type SCSettingCategories = string;
|
||||
*/
|
||||
export interface SCSettingWithoutReferences
|
||||
extends SCThingWithCategoriesWithoutReferences<SCSettingCategories, SCThingWithCategoriesSpecificValues> {
|
||||
|
||||
/**
|
||||
* The type of input/value this setting is carrying
|
||||
* The default value of a setting
|
||||
*/
|
||||
input: SCSettingInputType;
|
||||
defaultValue: SCSettingValue | SCSettingValues;
|
||||
/**
|
||||
* The input type of this setting
|
||||
*/
|
||||
inputType: SCSettingInputType;
|
||||
/**
|
||||
* The order number this setting should show up in its category list
|
||||
*/
|
||||
@@ -45,16 +48,26 @@ export interface SCSettingWithoutReferences
|
||||
* The type of this model
|
||||
*/
|
||||
type: SCThingType.Setting;
|
||||
/**
|
||||
* The key of a value of a setting
|
||||
*/
|
||||
value?: SCSettingValue | SCSettingValues;
|
||||
/**
|
||||
* The possible values of a setting
|
||||
*/
|
||||
values?: SCSettingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* The types of input/value a setting object can carry
|
||||
*/
|
||||
export type SCSettingInputType = SCSettingSingleChoice
|
||||
| SCSettingMultipleChoice
|
||||
| SCSettingNumber
|
||||
| SCSettingText
|
||||
| SCSettingPassword;
|
||||
export enum SCSettingInputType {
|
||||
SingleChoice = 'single choice',
|
||||
MultipleChoice = 'multiple choice',
|
||||
Number = 'number',
|
||||
Text = 'text',
|
||||
Password = 'password',
|
||||
}
|
||||
|
||||
/**
|
||||
* A setting with references
|
||||
@@ -69,58 +82,23 @@ export interface SCSetting extends SCSettingWithoutReferences {
|
||||
}
|
||||
|
||||
/**
|
||||
* Input type with single choice as value
|
||||
* The type a value of a setting can have
|
||||
*/
|
||||
export interface SCSettingSingleChoice {
|
||||
defaultValue: SCSettingValue;
|
||||
inputType: 'singleChoice';
|
||||
value?: SCSettingValue;
|
||||
values: SCSettingValue[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Input type with multiple choice as value
|
||||
*/
|
||||
export interface SCSettingMultipleChoice {
|
||||
defaultValue: SCSettingValue[];
|
||||
inputType: 'multipleChoice';
|
||||
value?: SCSettingValue[];
|
||||
values: SCSettingValue[];
|
||||
}
|
||||
|
||||
export type SCSettingValue = string | number | boolean;
|
||||
|
||||
/**
|
||||
* Input type with number as value
|
||||
* The type of multiple values a setting can have
|
||||
*/
|
||||
export interface SCSettingNumber {
|
||||
defaultValue: number;
|
||||
inputType: 'number';
|
||||
value?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input type with text as value
|
||||
*/
|
||||
export interface SCSettingText {
|
||||
defaultValue: string;
|
||||
inputType: 'text';
|
||||
value?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input type with secret text (eq. password) as value
|
||||
*/
|
||||
export interface SCSettingPassword {
|
||||
defaultValue: string;
|
||||
inputType: 'password';
|
||||
value?: string;
|
||||
}
|
||||
export type SCSettingValues = SCSettingValue[];
|
||||
|
||||
/**
|
||||
* Translatable properties of a setting
|
||||
*/
|
||||
export interface SCSettingValueTranslatableProperties extends SCThingWithCategoriesTranslatableProperties {
|
||||
/**
|
||||
* The translations of the possible values of a setting
|
||||
*/
|
||||
values?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,11 +113,17 @@ export class SCSettingMeta extends SCThingMeta implements SCMetaTranslations<SCS
|
||||
// tslint:disable-next-line:max-line-length
|
||||
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories,
|
||||
SCThingWithCategoriesSpecificValues>().fieldTranslations.de,
|
||||
defaultValue: 'Standard Wert',
|
||||
inputType: 'Eingabetyp',
|
||||
value: 'Wert',
|
||||
values: 'Werte',
|
||||
},
|
||||
en: {
|
||||
// tslint:disable-next-line:max-line-length
|
||||
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories,
|
||||
SCThingWithCategoriesSpecificValues>().fieldTranslations.en,
|
||||
defaultValue: 'default value',
|
||||
inputType: 'input type',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -151,6 +135,13 @@ export class SCSettingMeta extends SCThingMeta implements SCMetaTranslations<SCS
|
||||
// tslint:disable-next-line:max-line-length
|
||||
... SCThingWithCategoriesWithoutReferencesMeta.getInstance<SCSettingCategories,
|
||||
SCThingWithCategoriesSpecificValues>().fieldValueTranslations.de,
|
||||
inputType: {
|
||||
'multiple choice': 'mehrfach Auswahl',
|
||||
number: 'Zahl',
|
||||
password: 'Passwort',
|
||||
'single choice': 'einfache Auswahl',
|
||||
text: 'Text',
|
||||
},
|
||||
type: 'Einstellung',
|
||||
},
|
||||
en: {
|
||||
|
||||
171
src/core/things/StudyModule.ts
Normal file
171
src/core/things/StudyModule.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCAcademicPriceGroup,
|
||||
SCThingThatCanBeOffered,
|
||||
SCThingThatCanBeOfferedTranslatableProperties} from '../base/ThingThatCanBeOffered';
|
||||
import {SCThingMeta, SCThingType} from '../Thing';
|
||||
import {SCLanguage, SCMetaTranslations, SCTranslations} from '../types/i18n';
|
||||
import {SCMap} from '../types/Map';
|
||||
import {SCAcademicEventWithoutReferences} from './AcademicEvent';
|
||||
import {SCOrganizationWithoutReferences} from './Organization';
|
||||
import {SCPersonWithoutReferences} from './Person';
|
||||
|
||||
/**
|
||||
* A study module without references
|
||||
*/
|
||||
export interface SCStudyModuleWithoutReferences extends
|
||||
SCThingThatCanBeOffered<SCAcademicPriceGroup> {
|
||||
|
||||
/**
|
||||
* ECTS points (European Credit Transfer System)
|
||||
*/
|
||||
ects: number;
|
||||
|
||||
/**
|
||||
* The language in which the study module is offered
|
||||
*/
|
||||
language: SCLanguage;
|
||||
|
||||
/**
|
||||
* Majors that this study module is meant for
|
||||
*/
|
||||
majors: string[];
|
||||
|
||||
/**
|
||||
* Represents the modules necessity for each given major (of the major property)
|
||||
*/
|
||||
necessity: SCMap<SCStudyModuleNecessity>;
|
||||
|
||||
/**
|
||||
* Translated fields of a study module
|
||||
*/
|
||||
translations?: SCTranslations<SCStudyModuleTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the study module
|
||||
*/
|
||||
type: SCThingType.StudyModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* A study module
|
||||
*
|
||||
* @validatable
|
||||
*/
|
||||
export interface SCStudyModule extends SCStudyModuleWithoutReferences {
|
||||
/**
|
||||
* Academic events that make up a study module
|
||||
*/
|
||||
academicEvents: SCAcademicEventWithoutReferences[];
|
||||
|
||||
/**
|
||||
* The faculty that manages and curates the study module
|
||||
*/
|
||||
faculty: SCOrganizationWithoutReferences;
|
||||
|
||||
/**
|
||||
* Study modules needed for each others fulfillment
|
||||
*/
|
||||
partnerModules?: SCStudyModuleWithoutReferences[];
|
||||
|
||||
/**
|
||||
* Study modules required beforehand
|
||||
*/
|
||||
requiredModules?: SCStudyModuleWithoutReferences[];
|
||||
|
||||
/**
|
||||
* The secretary that administers requests and
|
||||
* questions concerning the study module by eg. students
|
||||
*/
|
||||
secretary: SCOrganizationWithoutReferences | SCPersonWithoutReferences;
|
||||
}
|
||||
|
||||
export interface SCStudyModuleTranslatableProperties
|
||||
extends SCThingThatCanBeOfferedTranslatableProperties {
|
||||
/**
|
||||
* Translations of the majors that this study module is meant for
|
||||
*/
|
||||
majors?: string[];
|
||||
|
||||
/**
|
||||
* Translations of the modules necessity for each given major (of the major property)
|
||||
*/
|
||||
necessity: SCMap<SCStudyModuleNecessity>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a modules necessity (in a major) as it may be required, optional or
|
||||
* is in a pool of n optional modules were m out of them have to be taken/completed.
|
||||
* Hence the elective option.
|
||||
*/
|
||||
export enum SCStudyModuleNecessity {
|
||||
Required = 'required',
|
||||
Elective = 'elective',
|
||||
Optional = 'optional',
|
||||
}
|
||||
|
||||
/**
|
||||
* Study module meta data
|
||||
*/
|
||||
export class SCStudyModuleMeta extends SCThingMeta implements SCMetaTranslations<SCStudyModule> {
|
||||
/**
|
||||
* Translations of fields
|
||||
*/
|
||||
fieldTranslations = {
|
||||
de: {
|
||||
... SCThingMeta.getInstance().fieldTranslations.de,
|
||||
academicEvents: 'Veranstaltungen',
|
||||
ects: 'ECTS-Punkte',
|
||||
faculty: 'Fachbereich',
|
||||
language: 'Unterrichtssprache',
|
||||
majors: 'Fachrichtungen',
|
||||
necessity: 'Erforderlichkeit',
|
||||
partnerModules: 'Partnermodule',
|
||||
requiredModules: 'Benötigte Module',
|
||||
secretary: 'Sekretariat',
|
||||
},
|
||||
en: {
|
||||
... SCThingMeta.getInstance().fieldTranslations.en,
|
||||
academicEvents: 'academic events',
|
||||
ects: 'ECTS points',
|
||||
faculty: 'faculty',
|
||||
language: 'teaching language',
|
||||
majors: 'majors',
|
||||
necessity: 'necessity',
|
||||
partnerModules: 'partner modules',
|
||||
requiredModules: 'required modules',
|
||||
secretary: 'secretary',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Translations of values of fields
|
||||
*/
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
... SCThingMeta.getInstance().fieldValueTranslations.de,
|
||||
necessity: {
|
||||
'elective' : 'Wahlfach',
|
||||
'optional' : 'optional',
|
||||
'required' : 'benötigt',
|
||||
},
|
||||
type: 'Studiengangmodul',
|
||||
},
|
||||
en: {
|
||||
... SCThingMeta.getInstance().fieldValueTranslations.en,
|
||||
type: SCThingType.StudyModule,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -13,11 +13,12 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {expect} from 'chai';
|
||||
import clone = require('fast-clone');
|
||||
import {slow, suite, test, timeout} from 'mocha-typescript';
|
||||
|
||||
import {SCThingOriginType, SCThingType} from '../src/core/Thing';
|
||||
import {SCBuildingWithoutReferences} from '../src/core/things/Building';
|
||||
import {SCDish, SCDishMeta} from '../src/core/things/Dish';
|
||||
import {SCPerson} from '../src/core/things/Person';
|
||||
import {SCThingTranslator} from '../src/core/Translator';
|
||||
|
||||
const building: SCBuildingWithoutReferences = {
|
||||
@@ -96,29 +97,15 @@ const dish: SCDish = {
|
||||
uid: '540862f3-ea30-5b8f-8678-56b4dc217140',
|
||||
};
|
||||
|
||||
const person: SCPerson = {
|
||||
familyName: 'base-familyName-name',
|
||||
givenName: 'base-givenName-name',
|
||||
homeLocations: [building, building, building],
|
||||
name : 'base-person-name',
|
||||
origin: {
|
||||
indexed: '1970',
|
||||
name: 'ding',
|
||||
type: SCThingOriginType.Remote,
|
||||
},
|
||||
type: SCThingType.Person,
|
||||
uid: '1234',
|
||||
};
|
||||
|
||||
const translator = new SCThingTranslator('de', 'en');
|
||||
const translator = new SCThingTranslator('de');
|
||||
// tslint:disable-next-line:no-eval
|
||||
const languageNonExistant = eval("'jp'");
|
||||
// this will simulate a translator always utilizing the base language translations
|
||||
const translatorWithFallback = new SCThingTranslator(languageNonExistant);
|
||||
|
||||
// tslint:disable:member-ordering TranslationSpec
|
||||
// tslint:disable:member-ordering TranslationSpecInplace
|
||||
@suite(timeout(10000), slow(5000))
|
||||
export class TranslationSpec {
|
||||
export class TranslationSpecInplace {
|
||||
|
||||
@test
|
||||
public directStringLiteralType() {
|
||||
@@ -231,134 +218,24 @@ export class TranslationSpec {
|
||||
expect(translator.translate(dish).offers[0].inPlace.categories[1234]('printer')).to.equal('printer');
|
||||
expect(translator.translate(dish).offers[0].inPlace.categories[1]('printer')).to.not.equal('printer');
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable:member-ordering TranslationSpecByString
|
||||
@suite(timeout(10000), slow(5000))
|
||||
export class TranslationSpecByString {
|
||||
|
||||
@test
|
||||
public directStringLiteralType() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'type')).to.equal('Essen');
|
||||
public changingTranslatorLanguageFlushesItsLRUCache() {
|
||||
const translatorDE = new SCThingTranslator('de');
|
||||
expect(translatorDE.translate(dish).name()).to.equal('de-dish-name');
|
||||
translatorDE.language = 'en';
|
||||
expect(translatorDE.translate(dish).name()).to.equal('base-dish-name');
|
||||
}
|
||||
|
||||
@test
|
||||
public directStringProperty() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'name')).to.equal('de-dish-name');
|
||||
}
|
||||
|
||||
@test
|
||||
public directArrayOfString() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'characteristics')).to.deep
|
||||
.equal([{name: 'de-characteristic0'}, {name: 'de-characteristic1'}]);
|
||||
}
|
||||
|
||||
@test
|
||||
public directArrayOfStringSubscript() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'characteristics[1]'))
|
||||
.to.deep.equal({name: 'de-characteristic1'});
|
||||
}
|
||||
|
||||
@test
|
||||
public directMetaArrayOfString() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'categories')).to.deep.equal(['Hauptgericht', 'Nachtisch']);
|
||||
}
|
||||
|
||||
@test
|
||||
public directMetaArrayOfStringSubscript() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'categories[1]')).to.equal('Nachtisch');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedStringLiteralType() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.type')).to.equal('Gebäude');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedStringProperty() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.name')).to.equal('de-space-name');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedMetaArrayOfString() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.categories'))
|
||||
.to.deep.equal(['Büro', 'Bildung']);
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedMetaArrayOfStringSubscript() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.categories[1]')).to.equal('Bildung');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedArrayOfStringSubscript() {
|
||||
expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.floors[1]')).to.equal('de-floor1');
|
||||
}
|
||||
|
||||
@test
|
||||
public directStringLiteralTypeFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'type')).to.equal('dish');
|
||||
}
|
||||
|
||||
@test
|
||||
public directStringPropertyFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'name')).to.equal('base-dish-name');
|
||||
}
|
||||
|
||||
@test
|
||||
public directArrayOfStringSubscriptFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'characteristics[1]'))
|
||||
.to.deep.equal({name: 'base-characteristic1'});
|
||||
}
|
||||
|
||||
@test
|
||||
public directMetaArrayOfStringFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'categories'))
|
||||
.to.deep.equal(['main dish', 'dessert']);
|
||||
}
|
||||
|
||||
@test
|
||||
public directMetaArrayOfStringSubscriptFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'categories[1]')).to.equal('dessert');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedStringLiteralTypeFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.type')).to.equal('building');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedStringPropertyFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.name')).to.equal('base-space-name');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedMetaArrayOfStringFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.categories'))
|
||||
.to.deep.equal(['office', 'education']);
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedMetaArrayOfStringSubscriptFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.categories[1]'))
|
||||
.to.equal('education');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedArrayOfStringSubscriptFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.floors[1]'))
|
||||
.to.equal('base-floor1');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedArrayOfStringSubscriptUncommonFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.floors.1')).to.equal('base-floor1');
|
||||
}
|
||||
|
||||
@test
|
||||
public nestedNestedMetaArrayOfStringSubscriptUncommonFallback() {
|
||||
expect(translatorWithFallback.getFieldValueTranslation(person, 'homeLocations.1.categories.1'))
|
||||
.to.equal('education');
|
||||
public forceTranslatorLRUCacheToOverflow() {
|
||||
const translatorDE = new SCThingTranslator('de');
|
||||
// Make sure to add more elements to the translator cache than the maximum cache capacity. See Translator.ts
|
||||
for (let i = 0; i < 201; i++) {
|
||||
const anotherDish = Object.assign({}, dish);
|
||||
anotherDish.uid = String(i);
|
||||
expect(translatorDE.translate(anotherDish).name()).to.equal('de-dish-name');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +253,7 @@ export class MetaTranslationSpec {
|
||||
|
||||
@test
|
||||
public thingWithoutMetaClass() {
|
||||
const dishCopy = Object.assign({}, dish);
|
||||
const dishCopy = clone(dish);
|
||||
const typeNonExistant = eval("(x) => x + 'typeNonExistant';");
|
||||
// this will assign a non existant SCThingType to dishCopy
|
||||
dishCopy.type = typeNonExistant();
|
||||
|
||||
@@ -5,15 +5,13 @@
|
||||
"privacy"
|
||||
],
|
||||
"description": "This is a Description",
|
||||
"input": {
|
||||
"defaultValue": "student",
|
||||
"inputType": "singleChoice",
|
||||
"values": [
|
||||
"student",
|
||||
"employee",
|
||||
"guest"
|
||||
]
|
||||
},
|
||||
"defaultValue": "student",
|
||||
"inputType": "single choice",
|
||||
"values": [
|
||||
"student",
|
||||
"employee",
|
||||
"guest"
|
||||
],
|
||||
"name": "group",
|
||||
"order": 0,
|
||||
"origin": {
|
||||
@@ -21,11 +19,6 @@
|
||||
"name": "Dummy",
|
||||
"type": "remote"
|
||||
},
|
||||
"translations": {
|
||||
"de": {
|
||||
"description": "Dies ist eine Beschreibung"
|
||||
}
|
||||
},
|
||||
"type": "setting",
|
||||
"uid": "c4ff2b08-be18-528e-9b09-cb8c1d18487b"
|
||||
},
|
||||
|
||||
@@ -5,20 +5,18 @@
|
||||
"privacy"
|
||||
],
|
||||
"description": "This is a Description",
|
||||
"input": {
|
||||
"defaultValue": [],
|
||||
"inputType": "multipleChoice",
|
||||
"values": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8
|
||||
]
|
||||
},
|
||||
"defaultValue": [],
|
||||
"inputType": "multiple choice",
|
||||
"values": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8
|
||||
],
|
||||
"name": "numbers",
|
||||
"order": 1,
|
||||
"origin": {
|
||||
@@ -26,16 +24,6 @@
|
||||
"name": "Dummy",
|
||||
"type": "remote"
|
||||
},
|
||||
"translations": {
|
||||
"de": {
|
||||
"description": "Dies ist eine Beschreibung",
|
||||
"name": "Nummern"
|
||||
},
|
||||
"en": {
|
||||
"description": "This is a Description",
|
||||
"name": "Numbers"
|
||||
}
|
||||
},
|
||||
"type": "setting",
|
||||
"uid": "9f0c362e-0b41-532f-9e8b-a0ac373fbede"
|
||||
},
|
||||
|
||||
47
test/resources/Setting.3.json
Normal file
47
test/resources/Setting.3.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"errorNames": [],
|
||||
"instance": {
|
||||
"categories": [
|
||||
"profile"
|
||||
],
|
||||
"defaultValue": "en",
|
||||
"description": "The language this app is going to use.",
|
||||
"inputType": "single choice",
|
||||
"name": "language",
|
||||
"order": 0,
|
||||
"origin": {
|
||||
"indexed": "2018-09-11T12:30:00Z",
|
||||
"name": "Dummy",
|
||||
"type": "remote"
|
||||
},
|
||||
"translations": {
|
||||
"de": {
|
||||
"categories": ["Benutzer"],
|
||||
"description": "Die Sprache in der die App angezeigt wird.",
|
||||
"name": "Sprache",
|
||||
"values": [
|
||||
"English",
|
||||
"German"
|
||||
]
|
||||
},
|
||||
"en": {
|
||||
"categories": [
|
||||
"User"
|
||||
],
|
||||
"description": "The language this app is going to use.",
|
||||
"name": "Language",
|
||||
"values": [
|
||||
"english",
|
||||
"german"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "setting",
|
||||
"values": [
|
||||
"en",
|
||||
"de"
|
||||
],
|
||||
"uid": "184b717a-d020-46f5-995c-03023670cc62"
|
||||
},
|
||||
"schema": "SCSetting"
|
||||
}
|
||||
Reference in New Issue
Block a user