Actually you have two options:
- Runtime check via validation methods
- Compile time check via typing functions
Runtime check
This one is pretty straightforward, just run all translations through some helper function, that throws error into the console, if translation is not present. But I think, vue-i18n already provides such functionality and you are here for option number 2.
Compile time check
You can make your translation function typesafe by providing types for incoming values.
Problem number one
Typing access to deeply nested structures via simple strings can be a really difficult task. If your goal is to type $t
the way it will not accept any strings other than paths present in your translations. It can only be done using new TypeScript 4.1 version. To update to latest version, run npm i -D typescript
.
I already asked similar question and got pretty far with this one. You can check out github repo of my properly-typed
project. Types, handy for your task can be found here.
It can look something like this:
type TranslationsStructure = {
Fields: {
Name: string,
LastName: string,
Direction: string,
ZipCode: string,
Phone: {
Number: string,
Prefix: string
}
}
}
const typedTranslate = <
T extends Record<string, unknown>,
P extends Leaves<T, D>,
D extends string = '.',
>(path: P): OutputType<T, P, D> => {
return /* use `$t(path)` here to get translation */path as any;
};
// correct, because `Fields.Name` path exists in `TranslationsStructure`
typedTranslate<TranslationsStructure, 'Fields.Name'>('Fields.Name');
// errors, because `Foo.bar` path exists in `TranslationsStructure`
typedTranslate<TranslationsStructure, 'Foo.bar'>('Foo.bar');
Playground link
Maybe I will release it as NPM package soon, when I will have a time to do so. I will keep this thread updated.
Problem number two
Different languages can have different translations missing. In this case you will need a typecheck, based on a chosen language. So your typedTranslate
function will need to have different types passed for different translations.
const en = {
Fields: {
Name: "Name",
LastName: "Last name",
Direction: "Full direction",
ZipCode: "Zip code",
Phone: {
Number: "Number",
Prefix: "Prefix"
}
}
};
const typedTranslate = <
T extends Record<string, unknown>,
P extends Leaves<T, D>,
D extends string = '.',
>(path: P): OutputType<T, P, D> => {
return /* use `$t(path)` here to get translation */path as any;
};
// correct, because `Fields.Name` path exists in `en` object
typedTranslate<typeof en, 'Fields.Name'>('Fields.Name')
// errors, because `Foo.bar` path exists in `en` object
typedTranslate<typeof en, 'Foo.bar'>('Foo.bar')
Playground link
Problem number three
As I see you have folder structures for each language, so you will need to aggregate it somehow into one object or type to use typedTranslate
function