{"record":{"id":"55b49f9e3e40727d","repo":"symfony/translation","slug":"the-pgs-switch-attribute-must-not-be-empty","errorCode":null,"errorMessage":"The pgs:switch attribute must not be empty.","messagePattern":"The pgs:switch attribute must not be empty\\.","errorType":"exception","errorClass":"InvalidResourceException","httpStatus":null,"severity":"error","filePath":"Loader/XliffFileLoader.php","lineNumber":256,"sourceCode":"        $metadata = ['pgs-switch' => $pgsSwitch];\n        if (isset($unit->notes)) {\n            $metadata['notes'] = [];\n            foreach ($unit->notes->note as $noteNode) {\n                $note = array_map('strval', $noteNode->attributes() ?? []);\n\n                $note['content'] = (string) $noteNode;\n                $metadata['notes'][] = $note;\n            }\n        }\n\n        $catalogue->setMetadata($source, $metadata, $intlDomain);\n    }\n\n    private function parsePgsSwitch(string $pgsSwitch): array\n    {\n        $trimmed = trim($pgsSwitch);\n        if ('' === $trimmed) {\n            throw new InvalidResourceException('The pgs:switch attribute must not be empty.');\n        }\n\n        $switches = [];\n        foreach (preg_split('/\\s+/', $trimmed) as $item) {\n            $parts = explode(':', $item, 2);\n            if (2 !== \\count($parts) || '' === $parts[0] || '' === $parts[1]) {\n                throw new InvalidResourceException(\\sprintf('The pgs:switch token \"%s\" must use the \"type:variable\" form.', $item));\n            }\n            $switches[] = ['type' => $parts[0], 'variable' => $parts[1]];\n        }\n\n        return $switches;\n    }\n\n    private function extractPgsSegmentText(\\SimpleXMLElement $element, array $switches): string\n    {\n        $pluralVariables = [];\n        foreach ($switches as $switch) {","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/symfony/translation/blob/ae9e8a51bc29780d63c7f9efd6005d7c875e100b/Loader/XliffFileLoader.php#L238-L274","documentation":"In XLIFF 2.0 files using the pgs (translation provenance/skeleton) extension, the pgs:switch attribute selects which sub-element to use. The loader throws InvalidResourceException when this attribute is present but empty (or whitespace only), because no switch criteria can be derived.","triggerScenarios":"Loading an XLIFF 2.0 file whose <unit> (pgs unit) carries pgs:switch=\"\" or pgs:switch=\"   \", reaching parsePgsSwitch via extractXliff2PgsUnit.","commonSituations":"Translation tooling that emits the pgs:switch attribute without populating it; hand-edited XLIFF where the variable mapping was deleted.","solutions":["Populate the pgs:switch attribute with space-separated type:variable tokens (e.g. \"env:BRANCH os:PLATFORM\").","Remove the pgs:switch attribute entirely if switching is not needed.","Open the file in the exporting tool and re-export with the extension settings completed."],"exampleFix":"// before\n<unit id=\"u1\" pgs:switch=\"\">...</unit>\n// after\n<unit id=\"u1\" pgs:switch=\"env:BRANCH\">...</unit>","handlingStrategy":"validation","validationCode":"$xml = simplexml_load_file($resource);\nforeach ($xml->xpath('//*[@pgs:switch]') ?: [] as $el) {\n    if ('' === trim((string) $el->attributes('pgs', true)->switch ?? '')) {\n        throw new \\RuntimeException('Empty pgs:switch in '.$resource);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $catalogue = $loader->load($resource, $locale);\n} catch (InvalidResourceException $e) {\n    if (str_contains($e->getMessage(), 'pgs:switch attribute must not be empty')) {\n        // fix or strip the pgs:switch attribute\n    }\n    throw $e;\n}","preventionTips":["Keep pgs extension attributes complete when the exporting tool emits them.","Strip unsupported/empty extension attributes before loading if unused.","Add schema/custom lint checks for pgs attributes in CI."],"tags":["php","xliff","xliff-2","invalid-attribute"],"backgroundTag":"empty-required-field","analyzedSha":"ae9e8a51bc29780d63c7f9efd6005d7c875e100b","analyzedAt":"2026-09-15T22:29:15.305Z","contentChangedAt":"2026-09-15T22:29:15.305Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}