-
-
Notifications
You must be signed in to change notification settings - Fork 342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interpolations without provided value should remain in the translation result #1638
Comments
Currently missing exactly this behaviour. E.g.: with the following translation key: en:
form.validations.xxx: '{label} should be at least {min} characters' In combination with Zod for validation: message: i18n.t("form.validations.xxx", {
min: issue.minimum
}) The actual outcome is: {{ t(errorMessage, {label, name, inputValue}, {resolvedMessage: true}) } My current work around is to manually map the missing keys back to their respective placeholders: message: i18n.t("form.validations.xxx", {
min: issue.minimum,
label: '{label}',
name: '{name}',
inputValue: '{inputValue}'
}) |
Interesting solution. But how do you perform tricks like this when the value of the message is unknown (dynamic key)? |
@youthug based on your question I felt like going for a bit of a puzzle as it sounded like a nice addition to our work around to be able to that. Here's what I came up with: utils/i18n/translateKeepMissing.ts import {
createParser,
type MessageNode,
type NamedNode,
type Node,
type PluralNode,
type ResourceNode,
} from "@intlify/message-compiler";
// See NodeTypes in "@intlify/message-compiler"
// Can't be imported due to '--isolatedModules' flag
const NodeTypes = {
Resource: 0,
Plural: 1,
Message: 2,
Text: 3,
Named: 4,
List: 5,
Linked: 6,
LinkedKey: 7,
LinkedModifier: 8,
Literal: 9,
} as const;
const isMessageNode = (n: Node): n is MessageNode => {
return n.type === NodeTypes.Message;
};
const isPluralNode = (n: Node): n is PluralNode => {
return n.type === NodeTypes.Plural;
};
const isNamedNode = (n: Node): n is NamedNode => {
return n.type === NodeTypes.Named;
};
const getNamed = (node: ResourceNode) => {
const placeholders: string[] = [];
const traverse = (n: MessageNode | PluralNode) => {
if (isMessageNode(n)) {
for (const item of n.items) {
if (isNamedNode(item)) {
placeholders.push(item.key);
}
}
}
if (isPluralNode(n)) {
for (const kase of n.cases) {
traverse(kase);
}
}
};
traverse(node.body);
return placeholders;
};
const p = createParser();
export default (key: string, named?: Record<string, unknown>) => {
const {tm, rt} = useI18n();
const rawLabel = tm(key) as string;
const body = p.parse(rawLabel);
const names = getNamed(body);
const providedNamedValues = named ? Object.keys(named) : [];
const unusedNames = names
.filter((name) => !providedNamedValues.includes(name))
.reduce(
(acc, name) => {
acc[name] = `{${name}}`;
return acc;
},
{} as Record<string, unknown>,
);
return rt(rawLabel, {
...named,
...unusedNames,
});
}; Then use it where ever you need it import translateKeepMissing from "~/utils/i18n/translateKeepMissing";
// Translate the message and provided it with a value for min - that's used if it exists in the label.
// Note that we do not provide it information about the missing properties like 'label'
const initialTranslation = translateKeepMissing("form.validations.xxx", {
min: 3,
}); // results in '{label} should be at least 3 characters' Then later at any other point you can re-interpolate the string like so const {t} = useI18n();
const finalTranslation = t(initialTranslation, {
label: "Subject",
}, {
resolvedMessage: true,
}); // results in 'Subject should be at least 3 characters' |
@JvanderHeide Thanks for the reply and your great work! I haven't tested the code yet, but it seems a bit tedious. I think it would be better if this was provided as a feature by vue-i18n. |
Can't fault you on that thought, I agree. Perhaps I have the time to dive in to that sometime in the future. |
Clear and concise description of the problem
We're upgrading to v9.x. When translating a string, we want to perform some extra logic on the result, using special tags
{tag}
to be replaced in the translated result. In V8, the translated value still contained those values, but in V9 it doesn't anymore.Input:
{ val: "I am a special {tag} string" }
, callingt("val")
.V8 result:
I am a special {tag} string
V9 result:
I am a special string
Suggested solution
Let's add an option to
createI18n
to toggle between those behaviors globally if it's not there.Alternative
No response
Additional context
No response
Validations
The text was updated successfully, but these errors were encountered: