From 1ebee93b9417c03da890f78d4cc4cc37019928cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Kuchy=C5=88ka=20=28Anty=29?= Date: Mon, 16 Mar 2026 11:45:19 +0100 Subject: [PATCH] feat: add variantOffsets to TolgeeFormat Expose the character offset of each plural variant's content within the source ICU string. Offsets are extracted from the Lezer parser's VariantContent node positions during plural parsing, ensuring they are always consistent with the variant text extraction. This is needed by the QA checks feature to map issue positions between per-variant and full-ICU coordinate spaces. --- src/parser/getTolgeePlurals.ts | 24 +++++++++++++++--------- src/parser/types.ts | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/parser/getTolgeePlurals.ts b/src/parser/getTolgeePlurals.ts index 3f0252a..7e9f0b1 100644 --- a/src/parser/getTolgeePlurals.ts +++ b/src/parser/getTolgeePlurals.ts @@ -26,18 +26,17 @@ function getVariantContent( variantNode: SyntaxNode, input: string, raw: boolean -) { +): { text: string; offset: number } { let node: SyntaxNode | null | undefined = variantNode.firstChild; do { if (node?.type.id === VariantContent) { - if (raw) { - return unescapeIcuAll(getNodeText(node, input)); - } else { - return getNodeText(node, input); - } + const text = raw + ? unescapeIcuAll(getNodeText(node, input)) + : getNodeText(node, input); + return { text, offset: node.from }; } } while ((node = node?.nextSibling)); - return ""; + return { text: "", offset: 0 }; } function returnNoPlural(value: string): TolgeeFormat { @@ -55,6 +54,7 @@ export const getTolgeePlurals = (input: string, raw: boolean): TolgeeFormat => { const nodes: Record = {}; const variants: Record = {}; + const offsets: Record = {}; const requiredIndexes = Object.keys(REQUIRED_NODES); const lastIndex = Number(requiredIndexes[requiredIndexes.length - 1]); @@ -94,16 +94,22 @@ export const getTolgeePlurals = (input: string, raw: boolean): TolgeeFormat => { let variant: SyntaxNode | null = nodes[SelectVariant]; do { const variantName = getNodeText(variant.firstChild!, input); - const variantContent = getVariantContent(variant, input, raw); + const { text: variantText, offset } = getVariantContent( + variant, + input, + raw + ); if (variants[variantName] !== undefined) { // two variants with the same name return returnNoPlural(input); } - variants[variantName] = variantContent; + variants[variantName] = variantText; + offsets[variantName] = offset; } while ((variant = variant.nextSibling)); return { parameter: getNodeText(nodes[Param], input), variants, + variantOffsets: offsets, }; }; diff --git a/src/parser/types.ts b/src/parser/types.ts index 63851a1..aa1d028 100644 --- a/src/parser/types.ts +++ b/src/parser/types.ts @@ -14,4 +14,5 @@ export type Placeholder = { export type TolgeeFormat = { parameter?: string; variants: Partial>; + variantOffsets?: Partial>; };