Skip to content

Commit

Permalink
feat: Allow i18n for JSONLD credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Feb 29, 2024
1 parent 82f82ef commit 1ce843e
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 1 deletion.
41 changes: 41 additions & 0 deletions packages/ssi-types/__tests__/language-values.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {mapLanguageValues,} from '../src'

const claims = {
langkey1: {language: "english", value: "yes1"},
langkey2: [{language: "english", value: "yes2"}, {language: "dutch", value: "ja2"}],
subLangClaim: {
langkey3: [{language: "English", value: "yes3"}, {language: "Dutch", value: "ja3"}],
random: "string",
object: {
language: "english", notvalue: "no"
},
anotherObject: {
myNumber: 1,
myNumberArray: [1, 2],
myString: "abc",
myStringArray: ["abc", "def"]
}
}
}
describe('Language object values', () => {
it('should convert claims into expected result ', () => {
const result = mapLanguageValues(claims)
expect(JSON.stringify(result)).toEqual({
"langkey1": "yes1",
"langkey2": "yes2",
"subLangClaim": {
"langkey3": "yes3",
"random": "string",
"object": {"language": "english", "notvalue": "no"},
"anotherObject": {
"myNumber": 1,
"myNumberArray": [1, 2],
"myString": "abc",
"myStringArray": ["abc", "def"]
}
}
})
})


})
1 change: 1 addition & 0 deletions packages/ssi-types/src/mapper/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './credential-mapper'
export * from './jsonld-language-values'
88 changes: 88 additions & 0 deletions packages/ssi-types/src/mapper/jsonld-language-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {ObjectUtils} from "../utils";

export type LanguageValueClaim = {
language: string
value: string | string[] | number | number[]
}

export const isLanguageValueObject = (claim?: unknown): claim is LanguageValueClaim => {
if (!claim || !ObjectUtils.isObject(claim)) {
return false
}
const keys = Object.keys(claim)
if (keys.length !== 2) {
return false // Only 'language' and 'value' for now
} else if (!('language' in claim && !!claim.language)) {
return false
} else if (!('value' in claim && !!claim.value)) {
return false
}
return true
}

export const isLanguageValueObjects = (claim?: unknown): claim is LanguageValueClaim[] => {
if (!Array.isArray(claim)) {
return false
}
return claim.every(val => isLanguageValueObject(val))
}

export const toLanguageValueObject = (claim?: unknown): LanguageValueClaim | undefined => {
return isLanguageValueObject(claim) ? claim : undefined
}

export const toLanguageValueObjects = (claim?: unknown): LanguageValueClaim[] | undefined => {
if (isLanguageValueObject(claim)) {
return [toLanguageValueObject(claim)!]
} else if (isLanguageValueObjects(claim)) {
return claim
}
return undefined // no empty array on purpose, as this really would not be a language value object
}


export const mapLanguageValue = (claim?: unknown, opts?: {
language?: string,
fallbackToFirstObject?: boolean
}): any => {
const langValues = toLanguageValueObjects(claim)
if (Array.isArray(langValues)) {
if (langValues.length === 0) {
// should not happen, but let's return original claim to be sure
return claim
}
const filteredLangValues = langValues.filter(val => opts?.language ? val.language.toLowerCase().includes(opts.language.toLowerCase()) : true)

let langValue: LanguageValueClaim
if (filteredLangValues.length > 0) {
langValue = filteredLangValues[0]
} else {
if (opts?.fallbackToFirstObject === false) {
// No match and we also do not fallback to the first value, so return the original claim
return claim
}
// Fallback to the first value
langValue = langValues[0]
}
return langValue.value
}

return claim
}


export const mapLanguageValues = (claimsOrCredential: object, opts?: {
language?: string,
fallbackToFirstObject?: boolean,
noDeepClone?: boolean
}): any => {
const result = opts?.noDeepClone ? {...claimsOrCredential} : JSON.parse(JSON.stringify(claimsOrCredential))
Object.keys(claimsOrCredential).every(key => {
result[key] = mapLanguageValue(result[key])
if (ObjectUtils.isObject(result[key]) || Array.isArray(result[key])) {
result[key] = mapLanguageValues(result[key], {...opts, noDeepClone: true})
}
})
return result
}

2 changes: 1 addition & 1 deletion packages/ssi-types/src/utils/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export class ObjectUtils {
return Array.isArray(value) ? value : [value]
}

public static isObject(value: unknown) {
public static isObject(value: unknown): value is object {
return Object.prototype.toString.call(value) === '[object Object]'
}

Expand Down

0 comments on commit 1ce843e

Please sign in to comment.