0 ? (this.value < 100 ? this.value : 100) : 0
}
get color(): ColorScheme {
- switch (this.type) {
- case 'default':
- return {
- outerBar: 'bg-gray-200',
- innerBar: 'bg-gray-100',
- text: 'text-gray-900',
- }
- case 'primary':
- return {
- outerBar: 'bg-primary',
- innerBar: 'bg-primary-lighter',
- text: 'text-white',
- }
- case 'secondary':
- return {
- outerBar: 'bg-secondary',
- innerBar: 'bg-secondary-lighter',
- text: 'text-white',
- }
- case 'light':
- return {
- outerBar: 'bg-primary-white',
- innerBar: 'bg-primary',
- text: 'text-main',
- }
+ const baseColor = (() => {
+ switch (this.type) {
+ case 'default':
+ return {
+ outerBar: 'bg-gray-200',
+ innerBar: 'bg-gray-100',
+ text: 'text-gray-900',
+ }
+ case 'primary':
+ return {
+ outerBar: 'bg-primary',
+ innerBar: 'bg-primary-lighter',
+ text: 'text-white',
+ }
+ case 'secondary':
+ return {
+ outerBar: 'bg-secondary',
+ innerBar: 'bg-secondary-lighter',
+ text: 'text-white',
+ }
+ case 'light':
+ return {
+ outerBar: 'bg-primary-white',
+ innerBar: 'bg-primary',
+ text: 'text-main',
+ }
+ }
+ })()
+
+ // Override innerBar color if barColor is provided
+ if (this.barColor) {
+ return {
+ ...baseColor,
+ innerBar: this.barColor,
+ }
}
+
+ return baseColor
}
}
diff --git a/libs/util/shared/src/lib/record/quality-score.util.spec.ts b/libs/util/shared/src/lib/record/quality-score.util.spec.ts
index da70418e9..acf9e663b 100644
--- a/libs/util/shared/src/lib/record/quality-score.util.spec.ts
+++ b/libs/util/shared/src/lib/record/quality-score.util.spec.ts
@@ -1,4 +1,4 @@
-import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record'
+import { CatalogRecord, DatasetDownloadDistribution, DatasetServiceDistribution, Individual, Keyword } from '@geonetwork-ui/common/domain/model/record'
import {
ValidatorMapperKeys,
@@ -11,23 +11,65 @@ describe('Metadata Validators', () => {
kind: 'dataset',
title: 'Test title',
abstract: 'Test abstract',
- keywords: ['keyword1', 'keyword2'],
- legalConstraints: [{ type: 'license', text: 'MIT' }],
+ keywords: [{ label: 'keyword1', type: 'other' }, { label: 'keyword2', type: 'other' }] as Keyword[],
+ legalConstraints: [{ text: 'MIT' }],
contacts: [
{
email: 'test@example.com',
organization: { name: 'Test Org' },
+ role: 'point_of_contact',
+ firstName: '',
+ lastName: '',
},
- ],
+ ] as Individual[],
+ contactsForResource: [
+ {
+ role: 'point_of_contact',
+ email: 'contact@example.com',
+ organization: { name: 'Test Org' },
+ firstName: '',
+ lastName: '',
+ },
+ {
+ role: 'owner',
+ email: 'owner@example.com',
+ organization: { name: 'Test Org' },
+ firstName: '',
+ lastName: '',
+ },
+ ] as Individual[],
updateFrequency: 'daily',
topics: ['environment'],
+ subtopics: ['subtopic1'],
onlineResources: [
{
- url: { href: 'http://example.com/capabilities' },
- },
+ type: 'download',
+ url: new URL('http://example.com/data.zip'),
+ } as DatasetDownloadDistribution,
+ {
+ type: 'service',
+ url: new URL('http://example.com/wms'),
+ accessServiceProtocol: 'wms',
+ } as DatasetServiceDistribution,
],
extras: {
sourcesIdentifiers: '12345',
+ resourceTitleObject: { langfre: 'Titre français', langger: 'Deutscher Titel' },
+ resourceAltTitleObject: [{ langfre: 'Alt titre FR', langger: 'Alt Titel DE' }],
+ resourceAbstractObject: {
+ langfre: 'Résumé français',
+ langger: 'Deutsche Zusammenfassung',
+ },
+ MD_LegalConstraintsOtherConstraintsObject: [{ default: 'CC-BY' }],
+ cl_statusObject: { key: 'completed' },
+ linkProtocol: ['MAP:Preview', 'OGC:WMS'],
+ featureTypes: [
+ {
+ typeName: 'Feature',
+ attributeTable: [{ name: 'ID', type: 'string' }],
+ },
+ ],
+ format: ['ESRI Shapefile (SHP)'],
},
}
@@ -38,8 +80,10 @@ describe('Metadata Validators', () => {
keywords: [],
legalConstraints: [],
contacts: [],
+ contactsForResource: [],
updateFrequency: undefined,
topics: [],
+ subtopics: [],
onlineResources: [],
extras: {},
}
@@ -49,162 +93,151 @@ describe('Metadata Validators', () => {
const result = getAllKeysValidator()
expect(result).toEqual([
'title',
+ 'titleMultilingual',
+ 'altTitleMultilingual',
'abstract',
+ 'abstractMultilingual',
'keywords',
'legalConstraints',
+ 'legalConstraintsOtherConstraints',
'contacts',
+ 'contactsPointOfContactEmail',
+ 'contactsForResourceWithOwner',
'updateFrequency',
'topics',
+ 'subtopics',
'organisation',
'source',
+ 'status',
+ 'linkDownload',
+ 'linkService',
+ 'linkMapPreview',
+ 'featureCatalog',
+ 'resourceFormat',
])
expect(result.every((key) => typeof key === 'string')).toBe(true)
})
})
describe('getQualityValidators', () => {
- const propsToValidate: ValidatorMapperKeys[] = [
- 'title',
- 'abstract',
- 'keywords',
- 'legalConstraints',
- 'contacts',
- 'updateFrequency',
- 'topics',
- 'organisation',
- 'source',
- ]
-
- it('should filter and return only validators applicable to record kind "dataset" with correct validation results', () => {
- const result = getQualityValidators(
- { ...mockRecord, kind: 'dataset' },
- propsToValidate
- )
-
- expect(result.length).toBe(8)
- expect(result[0].name).toBe('title')
- expect(result[1].name).toBe('abstract')
- expect(result[2].name).toBe('keywords')
- expect(result[3].name).toBe('legalConstraints')
- expect(result[4].name).toBe('contacts')
- expect(result[5].name).toBe('updateFrequency')
- expect(result[6].name).toBe('topics')
- expect(result[7].name).toBe('organisation')
- expect(result[0].validator()).toBe(true)
- expect(result[1].validator()).toBe(true)
- expect(result[2].validator()).toBe(true)
- expect(result[3].validator()).toBe(true)
- expect(result[4].validator()).toBe(true)
- expect(result[5].validator()).toBe(true)
- expect(result[6].validator()).toBe(true)
- expect(result[7].validator()).toBe(true)
-
- const resultFailedValidation = getQualityValidators(
- { ...mockRecordInvalid, kind: 'dataset' },
- propsToValidate
- )
-
- expect(resultFailedValidation.length).toBe(8)
- expect(resultFailedValidation[0].name).toBe('title')
- expect(resultFailedValidation[1].name).toBe('abstract')
- expect(resultFailedValidation[2].name).toBe('keywords')
- expect(resultFailedValidation[3].name).toBe('legalConstraints')
- expect(resultFailedValidation[4].name).toBe('contacts')
- expect(resultFailedValidation[5].name).toBe('updateFrequency')
- expect(resultFailedValidation[6].name).toBe('topics')
- expect(resultFailedValidation[7].name).toBe('organisation')
- expect(resultFailedValidation[0].validator()).toBe(false)
- expect(resultFailedValidation[1].validator()).toBe(false)
- expect(resultFailedValidation[2].validator()).toBe(false)
- expect(resultFailedValidation[3].validator()).toBe(false)
- expect(resultFailedValidation[4].validator()).toBe(false)
- expect(resultFailedValidation[5].validator()).toBe(false)
- expect(resultFailedValidation[6].validator()).toBe(false)
- expect(resultFailedValidation[7].validator()).toBe(false)
+ describe('for kind "dataset"', () => {
+ const propsToValidate: ValidatorMapperKeys[] = [
+ 'titleMultilingual',
+ 'altTitleMultilingual',
+ 'abstractMultilingual',
+ 'keywords',
+ 'legalConstraintsOtherConstraints',
+ 'contactsPointOfContactEmail',
+ 'contactsForResourceWithOwner',
+ 'status',
+ 'linkDownload',
+ 'linkService',
+ 'linkMapPreview',
+ 'subtopics',
+ 'organisation',
+ 'featureCatalog',
+ 'resourceFormat',
+ ]
+
+ it('should return all applicable validators passing with valid record', () => {
+ const result = getQualityValidators(
+ { ...mockRecord, kind: 'dataset' },
+ propsToValidate
+ )
+ expect(result.length).toBe(15)
+ expect(result.map((v) => v.name)).toEqual(propsToValidate)
+ result.forEach((v) => expect(v.validator()).toBe(true))
+ })
+
+ it('should return all applicable validators failing with invalid record', () => {
+ const result = getQualityValidators(
+ { ...mockRecordInvalid, kind: 'dataset' },
+ propsToValidate
+ )
+ expect(result.length).toBe(15)
+ result.forEach((v) => expect(v.validator()).toBe(false))
+ })
})
- it('should filter and return only validators applicable to record kind "reuse" with correct validation results', () => {
- const result = getQualityValidators(
- { ...mockRecord, kind: 'reuse' },
- propsToValidate
- )
-
- expect(result.length).toBe(8)
- expect(result[0].name).toBe('title')
- expect(result[1].name).toBe('abstract')
- expect(result[2].name).toBe('keywords')
- expect(result[3].name).toBe('legalConstraints')
- expect(result[4].name).toBe('contacts')
- expect(result[5].name).toBe('topics')
- expect(result[6].name).toBe('organisation')
- expect(result[7].name).toBe('source')
- expect(result[0].validator()).toBe(true)
- expect(result[1].validator()).toBe(true)
- expect(result[2].validator()).toBe(true)
- expect(result[3].validator()).toBe(true)
- expect(result[4].validator()).toBe(true)
- expect(result[5].validator()).toBe(true)
- expect(result[6].validator()).toBe(true)
- expect(result[7].validator()).toBe(true)
-
- const resultFailedValidation = getQualityValidators(
- { ...mockRecordInvalid, kind: 'reuse' },
- propsToValidate
- )
+ describe('for kind "reuse"', () => {
+ const propsToValidate: ValidatorMapperKeys[] = [
+ 'titleMultilingual',
+ 'altTitleMultilingual',
+ 'abstractMultilingual',
+ 'keywords',
+ 'legalConstraintsOtherConstraints',
+ 'contactsPointOfContactEmail',
+ 'contactsForResourceWithOwner',
+ 'status',
+ 'linkDownload',
+ 'linkService',
+ 'linkMapPreview',
+ 'subtopics',
+ 'organisation',
+ 'source',
+ ]
+
+ it('should return all applicable validators passing with valid record', () => {
+ const result = getQualityValidators(
+ { ...mockRecord, kind: 'reuse' },
+ propsToValidate
+ )
+ expect(result.length).toBe(14)
+ expect(result.map((v) => v.name)).toEqual(propsToValidate)
+ result.forEach((v) => expect(v.validator()).toBe(true))
+ })
+
+ it('should return all applicable validators failing with invalid record', () => {
+ const result = getQualityValidators(
+ { ...mockRecordInvalid, kind: 'reuse' },
+ propsToValidate
+ )
+ expect(result.length).toBe(14)
+ result.forEach((v) => expect(v.validator()).toBe(false))
+ })
+ })
- expect(resultFailedValidation.length).toBe(8)
- expect(resultFailedValidation[0].name).toBe('title')
- expect(resultFailedValidation[1].name).toBe('abstract')
- expect(resultFailedValidation[2].name).toBe('keywords')
- expect(resultFailedValidation[3].name).toBe('legalConstraints')
- expect(resultFailedValidation[4].name).toBe('contacts')
- expect(resultFailedValidation[5].name).toBe('topics')
- expect(resultFailedValidation[6].name).toBe('organisation')
- expect(resultFailedValidation[7].name).toBe('source')
- expect(resultFailedValidation[0].validator()).toBe(false)
- expect(resultFailedValidation[1].validator()).toBe(false)
- expect(resultFailedValidation[2].validator()).toBe(false)
- expect(resultFailedValidation[3].validator()).toBe(false)
- expect(resultFailedValidation[4].validator()).toBe(false)
- expect(resultFailedValidation[5].validator()).toBe(false)
- expect(resultFailedValidation[6].validator()).toBe(false)
- expect(resultFailedValidation[7].validator()).toBe(false)
+ describe('for kind "service"', () => {
+ const propsToValidate: ValidatorMapperKeys[] = [
+ 'titleMultilingual',
+ 'altTitleMultilingual',
+ 'abstractMultilingual',
+ 'keywords',
+ 'legalConstraintsOtherConstraints',
+ 'contactsPointOfContactEmail',
+ 'contactsForResourceWithOwner',
+ 'status',
+ 'linkDownload',
+ 'linkService',
+ 'linkMapPreview',
+ ]
+
+ it('should return all applicable validators passing with valid record', () => {
+ const result = getQualityValidators(
+ { ...mockRecord, kind: 'service' } as Partial,
+ propsToValidate
+ )
+ expect(result.length).toBe(11)
+ expect(result.map((v) => v.name)).toEqual(propsToValidate)
+ result.forEach((v) => expect(v.validator()).toBe(true))
+ })
+
+ it('should return all applicable validators failing with invalid record', () => {
+ const result = getQualityValidators(
+ { ...mockRecordInvalid, kind: 'service' } as Partial,
+ propsToValidate
+ )
+ expect(result.length).toBe(11)
+ result.forEach((v) => expect(v.validator()).toBe(false))
+ })
})
- it('should filter and return only validators applicable to record kind "service" with correct validation results', () => {
+ it('should exclude validators not applicable to the record kind', () => {
const result = getQualityValidators(
- { ...mockRecord, kind: 'service' },
- propsToValidate
+ { ...mockRecord, kind: 'service' } as Partial,
+ ['featureCatalog', 'resourceFormat', 'subtopics', 'source']
)
-
- expect(result.length).toBe(5)
- expect(result[0].name).toBe('title')
- expect(result[1].name).toBe('abstract')
- expect(result[2].name).toBe('keywords')
- expect(result[3].name).toBe('legalConstraints')
- expect(result[4].name).toBe('contacts')
- expect(result[0].validator()).toBe(true)
- expect(result[1].validator()).toBe(true)
- expect(result[2].validator()).toBe(true)
- expect(result[3].validator()).toBe(true)
- expect(result[4].validator()).toBe(true)
-
- const resultFailedValidation = getQualityValidators(
- { ...mockRecordInvalid, kind: 'service' },
- propsToValidate
- )
-
- expect(resultFailedValidation.length).toBe(5)
- expect(resultFailedValidation[0].name).toBe('title')
- expect(resultFailedValidation[1].name).toBe('abstract')
- expect(resultFailedValidation[2].name).toBe('keywords')
- expect(resultFailedValidation[3].name).toBe('legalConstraints')
- expect(resultFailedValidation[4].name).toBe('contacts')
- expect(resultFailedValidation[0].validator()).toBe(false)
- expect(resultFailedValidation[1].validator()).toBe(false)
- expect(resultFailedValidation[2].validator()).toBe(false)
- expect(resultFailedValidation[3].validator()).toBe(false)
- expect(resultFailedValidation[4].validator()).toBe(false)
+ expect(result.length).toBe(0)
})
})
})
diff --git a/libs/util/shared/src/lib/record/quality-score.util.ts b/libs/util/shared/src/lib/record/quality-score.util.ts
index 7f43fe7ab..e165270ab 100644
--- a/libs/util/shared/src/lib/record/quality-score.util.ts
+++ b/libs/util/shared/src/lib/record/quality-score.util.ts
@@ -9,21 +9,145 @@ type TValidatorMapper = {
const ValidatorMapper: TValidatorMapper = {
title: (record) => !!record?.title,
+ titleMultilingual: (record) => {
+ const titleObject = record?.extras?.resourceTitleObject as Record<
+ string,
+ unknown
+ > | undefined
+ const fre = titleObject?.['langfre'] as string | undefined
+ const ger = titleObject?.['langger'] as string | undefined
+ return !!(
+ fre &&
+ ger &&
+ fre.trim() !== ger.trim()
+ )
+ },
+ altTitleMultilingual: (record) => {
+ const altTitleArray = record?.extras?.resourceAltTitleObject as Array<
+ Record
+ > | undefined
+ return !!(
+ altTitleArray?.length &&
+ altTitleArray.some((item) => {
+ const langfre = item?.['langfre'] as string | undefined
+ const langger = item?.['langger'] as string | undefined
+ return (
+ langfre &&
+ langger &&
+ langfre.trim().length <= 35 &&
+ langger.trim().length <= 35 &&
+ langfre.trim() !== langger.trim()
+ )
+ })
+ )
+ },
abstract: (record) => !!record?.abstract,
- keywords: (record) => (record?.keywords?.length ?? 0) > 0,
+ abstractMultilingual: (record) => {
+ const abstractObject = record?.extras?.resourceAbstractObject as Record<
+ string,
+ unknown
+ > | undefined
+ const fre = abstractObject?.['langfre'] as string | undefined
+ const ger = abstractObject?.['langger'] as string | undefined
+ return !!(
+ fre &&
+ ger &&
+ fre.trim() !== ger.trim()
+ )
+ },
+ keywords: (record) => (record?.keywords?.length ?? 0) > 1,
legalConstraints: (record) =>
!!(
record?.legalConstraints?.length &&
record.legalConstraints.some((c) => c?.text?.trim().length > 0)
),
+ legalConstraintsOtherConstraints: (record) => {
+ const otherConstraints = record?.extras
+ ?.MD_LegalConstraintsOtherConstraintsObject as Array> | undefined
+ return !!(
+ otherConstraints?.length &&
+ otherConstraints.some((c) =>
+ Object.values(c).some(
+ (val) => typeof val === 'string' && val.trim().length > 0
+ )
+ )
+ )
+ },
contacts: (record) =>
!!record?.contacts?.[0]?.email &&
record.contacts[0].email !== 'missing@missing.com',
+ contactsPointOfContactEmail: (record) => {
+ const isValidEmail = (email: string) => {
+ if (!email || typeof email !== 'string') return false
+ email = email.trim()
+ if (email.length < 5 || email.length > 254) return false
+
+ const dummyPatterns =
+ /^(test|example|demo|placeholder|dummy|fake|unknown|noemail|contact@contact)$/i
+ if (dummyPatterns.test(email)) return false
+
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
+ }
+ return !!(
+ record?.contactsForResource?.length &&
+ record.contactsForResource.some(
+ (c) =>
+ c?.role === 'point_of_contact' &&
+ c?.email &&
+ isValidEmail(c.email as string)
+ )
+ )
+ },
+ contactsForResourceWithOwner: (record) =>
+ !!(
+ record?.contactsForResource?.length &&
+ record.contactsForResource.some((c) => c?.role === 'owner')
+ ),
updateFrequency: (record) =>
!!record?.updateFrequency && record.updateFrequency !== 'unknown',
topics: (record) => (record?.topics?.length ?? 0) > 0,
+ subtopics: (record) => (record?.subtopics?.length ?? 0) > 0,
organisation: (record) => !!record?.contacts?.[0]?.organization?.name,
source: (record) => !!record?.extras?.sourcesIdentifiers,
+ status: (record) => {
+ const statusObject = record?.extras?.cl_statusObject as Record<
+ string,
+ unknown
+ > | undefined
+ return !!statusObject
+ },
+ linkDownload: (record) =>
+ !!(record?.onlineResources?.some((r) => r.type === 'download')),
+ linkService: (record) =>
+ !!(record?.onlineResources?.some((r) => r.type === 'service' || r.type === 'endpoint')),
+ linkMapPreview: (record) => {
+ const linkProtocols = record?.extras?.linkProtocol as
+ | string[]
+ | undefined
+ return !!(
+ linkProtocols?.some(
+ (p) =>
+ p?.startsWith('MAP:') || p === 'CHTOPO:specialised-geoportal'
+ )
+ )
+ },
+ featureCatalog: (record) => {
+ const featureTypes = record?.extras?.['featureTypes'] as
+ | Array<{ attributeTable?: unknown[]; attributes?: unknown[] }>
+ | undefined
+ const hasEmbedded = !!(featureTypes?.some(
+ (ft) => (ft?.attributeTable?.length ?? ft?.attributes?.length ?? 0) > 0
+ ))
+ const hasExternal = !!record?.extras?.['featureCatalogIdentifier']
+ return hasEmbedded || hasExternal
+ },
+ resourceFormat: (record) => {
+ const formats = record?.extras?.format as string[] | undefined
+ return !!(formats?.some((f) => typeof f === 'string' && f.trim().length > 0))
+ },
} as const
export type ValidatorMapperKeys = keyof typeof ValidatorMapper & string
@@ -35,23 +159,43 @@ export function getAllKeysValidator() {
function getMappersFromKind(kind: RecordKind) {
let kindKeys = []
const commonsKeys = [
- 'title',
- 'abstract',
+ 'titleMultilingual',
+ 'altTitleMultilingual',
+ 'abstractMultilingual',
'keywords',
- 'legalConstraints',
- 'contacts',
+ 'subtopics',
+ 'organisation',
+ 'linkDownload',
+ 'linkService',
+ 'linkMapPreview',
]
switch (kind) {
case 'reuse':
- kindKeys = ['topics', 'organisation', 'source']
+ kindKeys = [
+ 'source'
+ ]
break
case 'service':
- kindKeys = []
+ kindKeys = [
+ 'featureCatalog',
+ 'resourceFormat',
+ 'legalConstraintsOtherConstraints',
+ 'contactsPointOfContactEmail',
+ 'contactsForResourceWithOwner',
+ 'status'
+ ]
break
case 'dataset':
default:
- kindKeys = ['updateFrequency', 'topics', 'organisation']
+ kindKeys = [
+ 'featureCatalog',
+ 'resourceFormat',
+ 'legalConstraintsOtherConstraints',
+ 'contactsPointOfContactEmail',
+ 'contactsForResourceWithOwner',
+ 'status'
+ ]
}
return [...commonsKeys, ...kindKeys]
diff --git a/translations/de.json b/translations/de.json
index b808896ba..05b7745f4 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -505,20 +505,49 @@
"record.metadata.quality.contacts.failed": "Kontakt nicht angegeben",
"record.metadata.quality.contacts.success": "Kontakt angegeben",
"record.metadata.quality.details": "Details",
- "record.metadata.quality.keywords.failed": "Schlagwörter nicht angegeben",
- "record.metadata.quality.keywords.success": "Schlagwörter angegeben",
+ "record.metadata.quality.keywords.failed": "Schlagwörter nicht angegeben (mindestens zwei erforderlich)",
+ "record.metadata.quality.keywords.success": "Schlagwörter angegeben (mindestens zwei erforderlich)",
"record.metadata.quality.legalConstraints.failed": "Rechtliche Beschränkungen nicht angegeben",
"record.metadata.quality.legalConstraints.success": "Rechtliche Beschränkungen angegeben",
+ "record.metadata.quality.legalConstraintsOtherConstraints.failed": "Nutzungsbedingungen von opendata.swiss nicht angegeben",
+ "record.metadata.quality.legalConstraintsOtherConstraints.success": "Nutzungsbedingungen von opendata.swiss angegeben",
"record.metadata.quality.organisation.failed": "Organisation nicht angegeben",
"record.metadata.quality.organisation.success": "Organisation angegeben",
"record.metadata.quality.source.failed": "Keine Quelldatensätze angegeben",
"record.metadata.quality.source.success": "Ein Quelldatensatz wird ausgefüllt",
"record.metadata.quality.title.failed": "Titel nicht angegeben",
"record.metadata.quality.title.success": "Titel angegeben",
+ "record.metadata.quality.titleMultilingual.failed": "Titel nicht in Französisch und Deutsch vorhanden",
+ "record.metadata.quality.titleMultilingual.success": "Titel auf Französisch und Deutsch vorhanden",
+ "record.metadata.quality.abstractMultilingual.failed": "Beschreibung nicht in Französisch und Deutsch vorhanden",
+ "record.metadata.quality.abstractMultilingual.success": "Beschreibung auf Französisch und Deutsch vorhanden",
+ "record.metadata.quality.altTitleMultilingual.failed": "Alternativer Titel nicht in Französisch und Deutsch vorhanden (max. 35 Zeichen)",
+ "record.metadata.quality.altTitleMultilingual.success": "Alternativer Titel auf Französisch und Deutsch vorhanden (max. 35 Zeichen)",
+ "record.metadata.quality.contactsForResourceWithOwner.failed": "Kein Eigentümer für die Ressource angegeben",
+ "record.metadata.quality.contactsForResourceWithOwner.success": "Eigentümer für die Ressource angegeben",
+ "record.metadata.quality.contactsPointOfContactEmail.failed": "Die E-Mail des Ansprechpartners für die Ressource ist ungültig oder nicht angegeben",
+ "record.metadata.quality.contactsPointOfContactEmail.success": "Die E-Mail des Ansprechpartners für die Ressource ist gültig und angegeben",
"record.metadata.quality.topics.failed": "Thema nicht angegeben",
- "record.metadata.quality.topics.success": "Thema angegeben",
+ "record.metadata.quality.topics.success": "Thema angegeben", "record.metadata.quality.subtopics.failed": "Unterthema nicht angegeben",
+ "record.metadata.quality.subtopics.success": "Unterthema angegeben",
+ "record.metadata.quality.status.failed": "Status nicht angegeben",
+ "record.metadata.quality.status.success": "Status angegeben",
+ "record.metadata.quality.linkDownload.failed": "Kein Download-Link angegeben",
+ "record.metadata.quality.linkDownload.success": "Ein Download-Link ist angegeben",
+ "record.metadata.quality.linkService.failed": "Kein Kartendienst-Link angegeben (WMS, WMTS, ESRI:REST...)",
+ "record.metadata.quality.linkService.success": "Ein Kartendienst-Link ist angegeben (WMS, WMTS, ESRI:REST...)",
+ "record.metadata.quality.linkMapPreview.failed": "Kein Kartenvorschau-Link angegeben",
+ "record.metadata.quality.linkMapPreview.success": "Ein Kartenvorschau-Link ist angegeben",
+ "record.metadata.quality.featureCatalog.failed": "Kein Attributkatalog angegeben",
+ "record.metadata.quality.featureCatalog.success": "Ein Attributkatalog ist angegeben",
+ "record.metadata.quality.resourceFormat.failed": "Kein Download-Format angegeben",
+ "record.metadata.quality.resourceFormat.success": "Ein Download-Format ist angegeben",
"record.metadata.quality.updateFrequency.failed": "Aktualisierungsfrequenz nicht angegeben",
"record.metadata.quality.updateFrequency.success": "Aktualisierungsfrequenz angegeben",
+ "record.metadata.quality.score.insufficient": "Unzureichend",
+ "record.metadata.quality.score.average": "Durchschnittlich",
+ "record.metadata.quality.score.good": "Gut",
+ "record.metadata.quality.score.excellent": "Sehr gut",
"record.metadata.related": "Entdecken Sie den Katalog",
"record.metadata.resourceCreated": "Erstellt",
"record.metadata.resourcePublished": "Veröffentlicht",
diff --git a/translations/en.json b/translations/en.json
index 70292fd08..96298bd32 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -500,25 +500,55 @@
"record.metadata.quality": "Metadata Quality",
"record.metadata.quality.abstract.failed": "Description is not specified",
"record.metadata.quality.abstract.success": "Description is specified",
- "record.metadata.quality.capabilities.failed": "Capabilities URL is not specified",
- "record.metadata.quality.capabilities.success": "Capabilities URL is specified",
+ "record.metadata.quality.capabilities.failed": "No Capabilities URL is specified",
+ "record.metadata.quality.capabilities.success": "A Capabilities URL is specified",
"record.metadata.quality.contacts.failed": "Contact is not specified",
"record.metadata.quality.contacts.success": "Contact is specified",
"record.metadata.quality.details": "Details",
- "record.metadata.quality.keywords.failed": "Keywords are not specified",
- "record.metadata.quality.keywords.success": "Keywords are specified",
+ "record.metadata.quality.keywords.failed": "Keywords are not specified (at least two are required)",
+ "record.metadata.quality.keywords.success": "Keywords are specified (at least two are required)",
"record.metadata.quality.legalConstraints.failed": "Legal constraints are not specified",
"record.metadata.quality.legalConstraints.success": "Legal constraints are specified",
- "record.metadata.quality.organisation.failed": "Organisation is not specified",
- "record.metadata.quality.organisation.success": "Organisation is specified",
+ "record.metadata.quality.legalConstraintsOtherConstraints.failed": "Opendata.swiss usage conditions are not specified",
+ "record.metadata.quality.legalConstraintsOtherConstraints.success": "Opendata.swiss usage conditions are specified",
+ "record.metadata.quality.organisation.failed": "Producer is not specified",
+ "record.metadata.quality.organisation.success": "Producer is specified",
"record.metadata.quality.source.failed": "No source dataset is specified",
"record.metadata.quality.source.success": "A source dataset is specified",
"record.metadata.quality.title.failed": "Title is not specified",
"record.metadata.quality.title.success": "Title is specified",
- "record.metadata.quality.topics.failed": "Topic is not specified",
- "record.metadata.quality.topics.success": "Topic is specified",
- "record.metadata.quality.updateFrequency.failed": "Update frequency is not specified",
- "record.metadata.quality.updateFrequency.success": "Update frequency is specified",
+ "record.metadata.quality.titleMultilingual.failed": "Title is not specified in French and German",
+ "record.metadata.quality.titleMultilingual.success": "Title is specified in French and German",
+ "record.metadata.quality.abstractMultilingual.failed": "Description is not specified in French and German",
+ "record.metadata.quality.abstractMultilingual.success": "Description is specified in French and German",
+ "record.metadata.quality.altTitleMultilingual.failed": "Alternative title is not specified in French and German (max 35 characters)",
+ "record.metadata.quality.altTitleMultilingual.success": "Alternative title is specified in French and German (max 35 characters)",
+ "record.metadata.quality.contactsForResourceWithOwner.failed": "No owner is specified for the resource",
+ "record.metadata.quality.contactsForResourceWithOwner.success": "An owner is specified for the resource",
+ "record.metadata.quality.contactsPointOfContactEmail.failed": "The contact point email is not valid or not specified for the resource",
+ "record.metadata.quality.contactsPointOfContactEmail.success": "The contact point email is valid and specified for the resource",
+ "record.metadata.quality.topics.failed": "The topic is not specified",
+ "record.metadata.quality.topics.success": "The topic is specified",
+ "record.metadata.quality.subtopics.failed": "The subtopic is not specified",
+ "record.metadata.quality.subtopics.success": "The subtopic is specified",
+ "record.metadata.quality.status.failed": "The status is not specified",
+ "record.metadata.quality.status.success": "The status is specified",
+ "record.metadata.quality.linkDownload.failed": "No download link is specified",
+ "record.metadata.quality.linkDownload.success": "A download link is specified",
+ "record.metadata.quality.linkService.failed": "No map service link is specified (WMS, WMTS, ESRI:REST...)",
+ "record.metadata.quality.linkService.success": "A map service link is specified (WMS, WMTS, ESRI:REST...)",
+ "record.metadata.quality.linkMapPreview.failed": "No map preview link is specified",
+ "record.metadata.quality.linkMapPreview.success": "A map preview link is specified",
+ "record.metadata.quality.featureCatalog.failed": "No feature catalog (attribute table) is specified",
+ "record.metadata.quality.featureCatalog.success": "A feature catalog (attribute table) is specified",
+ "record.metadata.quality.resourceFormat.failed": "No download format is specified",
+ "record.metadata.quality.resourceFormat.success": "A download format is specified",
+ "record.metadata.quality.updateFrequency.failed": "The update frequency is not specified",
+ "record.metadata.quality.updateFrequency.success": "The update frequency is specified",
+ "record.metadata.quality.score.insufficient": "Insufficient",
+ "record.metadata.quality.score.average": "Average",
+ "record.metadata.quality.score.good": "Good",
+ "record.metadata.quality.score.excellent": "Excellent",
"record.metadata.related": "Explore the catalog",
"record.metadata.resourceCreated": "Created",
"record.metadata.resourcePublished": "Published",
diff --git a/translations/fr.json b/translations/fr.json
index 0bf6a1b0e..3b94fa7c4 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -497,7 +497,7 @@
"record.metadata.producer": "Producteur de la donnée",
"record.metadata.publication": "Date de publication",
"record.metadata.publications": "{count, plural, =0{ressource} one{ressource} other{ressources}}",
- "record.metadata.quality": "Description de la ressource",
+ "record.metadata.quality": "Qualité de la métadonnée",
"record.metadata.quality.abstract.failed": "La description n'est pas renseignée",
"record.metadata.quality.abstract.success": "La description est renseignée",
"record.metadata.quality.capabilities.failed": "Aucune URL de Capabilities n'est renseignée",
@@ -505,20 +505,50 @@
"record.metadata.quality.contacts.failed": "Le contact n'est pas renseigné",
"record.metadata.quality.contacts.success": "Le contact est renseigné",
"record.metadata.quality.details": "Détails",
- "record.metadata.quality.keywords.failed": "Les mots-clés ne sont pas renseignés",
- "record.metadata.quality.keywords.success": "Les mots-clés sont renseignés",
+ "record.metadata.quality.keywords.failed": "Les mots-clés ne sont pas renseignés (au moins deux sont nécessaires)",
+ "record.metadata.quality.keywords.success": "Les mots-clés sont renseignés (au moins deux sont nécessaires)",
"record.metadata.quality.legalConstraints.failed": "Les contraintes légales ne sont pas renseignées",
"record.metadata.quality.legalConstraints.success": "Les contraintes légales sont renseignées",
+ "record.metadata.quality.legalConstraintsOtherConstraints.failed": "Les conditions d'utilisation opendata.swiss ne sont pas renseignées",
+ "record.metadata.quality.legalConstraintsOtherConstraints.success": "Les conditions d'utilisation opendata.swiss sont renseignées",
"record.metadata.quality.organisation.failed": "Le producteur n'est pas renseigné",
"record.metadata.quality.organisation.success": "Le producteur est renseigné",
"record.metadata.quality.source.failed": "Aucun jeu de données source n'est renseigné",
"record.metadata.quality.source.success": "Un jeu de données source est renseigné",
"record.metadata.quality.title.failed": "Le titre n'est pas renseigné",
"record.metadata.quality.title.success": "Le titre est renseigné",
+ "record.metadata.quality.titleMultilingual.failed": "Le titre n'est pas renseigné en français et allemand",
+ "record.metadata.quality.titleMultilingual.success": "Le titre est renseigné en français et allemand",
+ "record.metadata.quality.abstractMultilingual.failed": "La description n'est pas renseignée en français et allemand",
+ "record.metadata.quality.abstractMultilingual.success": "La description est renseignée en français et allemand",
+ "record.metadata.quality.altTitleMultilingual.failed": "Le titre alternatif n'est pas renseigné en français et allemand (max 35 caractères)",
+ "record.metadata.quality.altTitleMultilingual.success": "Le titre alternatif est renseigné en français et allemand (max 35 caractères)",
+ "record.metadata.quality.contactsForResourceWithOwner.failed": "Aucun propriétaire n'est renseigné pour la ressource",
+ "record.metadata.quality.contactsForResourceWithOwner.success": "Un propriétaire est renseigné pour la ressource",
+ "record.metadata.quality.contactsPointOfContactEmail.failed": "L'email du point de contact n'est pas valide ou n'est pas renseigné pour la ressource",
+ "record.metadata.quality.contactsPointOfContactEmail.success": "L'email du point de contact est valide et renseigné pour la ressource",
"record.metadata.quality.topics.failed": "Le thème n'est pas renseigné",
"record.metadata.quality.topics.success": "Le thème est renseigné",
+ "record.metadata.quality.subtopics.failed": "Le sous-thème n'est pas renseigné",
+ "record.metadata.quality.subtopics.success": "Le sous-thème est renseigné",
+ "record.metadata.quality.status.failed": "Le statut n'est pas renseigné",
+ "record.metadata.quality.status.success": "Le statut est renseigné",
+ "record.metadata.quality.linkDownload.failed": "Aucun lien de téléchargement n'est renseigné",
+ "record.metadata.quality.linkDownload.success": "Un lien de téléchargement est renseigné",
+ "record.metadata.quality.linkService.failed": "Aucun lien vers un service cartographique n'est renseigné (WMS, WMTS, ESRI:REST...)",
+ "record.metadata.quality.linkService.success": "Un lien vers un service cartographique est renseigné (WMS, WMTS, ESRI:REST...)",
+ "record.metadata.quality.linkMapPreview.failed": "Aucun lien de prévisualisation cartographique n'est renseigné",
+ "record.metadata.quality.linkMapPreview.success": "Un lien de prévisualisation cartographique est renseigné",
+ "record.metadata.quality.featureCatalog.failed": "Aucun catalogue d'attributs n'est renseigné",
+ "record.metadata.quality.featureCatalog.success": "Un catalogue d'attributs est renseigné",
+ "record.metadata.quality.resourceFormat.failed": "Aucun format de téléchargement n'est renseigné",
+ "record.metadata.quality.resourceFormat.success": "Un format de téléchargement est renseigné",
"record.metadata.quality.updateFrequency.failed": "La fréquence de mise à jour n'est pas renseignée",
"record.metadata.quality.updateFrequency.success": "La fréquence de mise à jour est renseignée",
+ "record.metadata.quality.score.insufficient": "Insuffisant",
+ "record.metadata.quality.score.average": "Moyen",
+ "record.metadata.quality.score.good": "Bon",
+ "record.metadata.quality.score.excellent": "Très bon",
"record.metadata.related": "Explorez le catalogue",
"record.metadata.resourceCreated": "Créé",
"record.metadata.resourcePublished": "Publié",