i18next i18next is a package to translate a website Resources translations are kept in resources property usually translation files are kept in deferent files (objects) which makes its maintenance a bit difficult
i18next.init({
resources: {
en: {
translation: {
"key": "hello"
}
},
fi: {
translation: {
"key": "moi"
}
}
}
})
we may keep translations in one object and transform it to the required shape
const resources = {
fi: { translation: {} },
en: { translation: {} },
}
type Lang = keyof typeof resources
type NestedObject<T = string> = {
[key: string]: T | NestedObject<T>
}
type Translations = NestedObject<Record<Lang, string>>
const translations: Translations = {
nav: {
key1: {
en: "value 1 en",
fi: "value 1 fi"
},
key2: {
en: "value 2 en",
fi: "value 2 fi"
},
someMenu: {
key3: {
en: "value 3 en",
fi: "value 3 fi"
},
key4: {
en: "value 4 en",
fi: "value 4 fi"
}
}
},
form: {
key5: {
en: "value 5 en",
fi: "value 5 fi"
},
key6: {
en: "value 6 en",
fi: "value 6 fi"
}
}
}
const processNestedObject = (nestedObject: Translations | NestedObject, lang: Lang): NestedObject => {
const transformedObject: NestedObject = {}
for (const key in nestedObject) {
const value = nestedObject[key]
if (typeof value === 'object' && Object.hasOwn(value, lang)) {
Object.assign(transformedObject, { [key]: value[lang] })
}
if (typeof value === 'object' && !Object.hasOwn(value, lang)) {
transformedObject[key] = processNestedObject(value, lang)
}
}
return transformedObject
}
(Object.keys(resources) as Lang[]).forEach(lang => {
resources[lang].translation = processNestedObject(translations, lang)
})
export { resources }
// output
en: {
translation: {
nav: {
key1: 'value 1 en',
key2: 'value 2 en',
someMenu: {
key3: 'value 3 en',
key4: 'value 4 en'
}
},
form: {
key5: 'value 5 en',
key6: 'value 6 en'
},
},
},
fi: {
translation: {
nav: {
key1: 'value 1 fi',
key2: 'value 2 fi',
someMenu: {
key3: 'value 3 fi',
key4: 'value 4 fi'
}
},
form: {
key5: 'value 5 fi',
key6: 'value 6 fi'
}
}
},