{"record":{"id":"34957713c689e98c","repo":"Humanizr/Humanizer","slug":"phrase-path-cannot-use-numeric-placeholder","errorCode":null,"errorMessage":"Phrase '{path}' cannot use numeric placeholder '{{placeholder}}'.","messagePattern":"Phrase '(.+?)' cannot use numeric placeholder '(.+?)\\}'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs","lineNumber":517,"sourceCode":"            }\n\n            var templateMapping = ExpectMapping(value, path);\n            RejectUnknownKeys(templateMapping, path, [\"template\"]);\n            if (!templateMapping.TryGetValue(\"template\", out var templateValue))\n            {\n                throw new InvalidOperationException($\"Phrase '{path}' must define a scalar string or an explicit 'template'.\");\n            }\n\n            return ParseNamedTemplate(templateValue, $\"{path}.template\", [\"value\"]).Template;\n        }\n\n        static string ValidateLiteralText(string path, string text, params string[] allowedPlaceholders)\n        {\n            foreach (var placeholder in GetPlaceholderNames(text))\n            {\n                if (int.TryParse(placeholder, out _))\n                {\n                    throw new InvalidOperationException($\"Phrase '{path}' cannot use numeric placeholder '{{{placeholder}}}'.\");\n                }\n\n                if (placeholder.Length == 0 || !char.IsLetter(placeholder[0]) || placeholder.Any(static c => !char.IsLetterOrDigit(c) && c != '_'))\n                {\n                    throw new InvalidOperationException($\"Phrase '{path}' uses unsupported placeholder '{{{placeholder}}}'.\");\n                }\n\n                if (!allowedPlaceholders.Contains(placeholder, StringComparer.Ordinal))\n                {\n                    throw new InvalidOperationException(\n                        allowedPlaceholders.Length == 0\n                            ? $\"Phrase '{path}' can only use placeholders in explicit 'template' entries.\"\n                            : $\"Phrase '{path}' can only use placeholders: {string.Join(\", \", allowedPlaceholders.Select(static value => $\"{{{value}}}\"))}.\");\n                }\n            }\n\n            return text;\n        }","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs#L499-L535","documentation":"Thrown by ValidateLiteralText when a literal phrase string (a non-template field like singular, default, beforeCount) contains a numeric placeholder such as {0}. Literals do not support positional placeholders; only explicit 'template' entries do formatting. Surfaced as compiler diagnostic HSG003 (severity Error).","triggerScenarios":"Writing 'default: {0} hours' or 'beforeCount: {0}' in any phrase literal field.","commonSituations":"Copy-paste from string.Format-based resources; habit from other i18n libraries; converting old code that used numeric substitution.","solutions":["Remove the placeholder from the literal, or move the formatting into an explicit 'template' entry that uses named placeholders.","If the count must appear in the phrase, use countPlacement + forms instead of inlining {0}.","Rebuild to confirm the diagnostic clears."],"exampleFix":"# before\nduration:\n  hour:\n    multiple:\n      forms:\n        default: '{0} hours'\n# after\nduration:\n  hour:\n    multiple:\n      countPlacement: before\n      forms:\n        default: hours","handlingStrategy":"validation","validationCode":"# Find numeric placeholders in literal (non-template) string fields.\ngrep -nE '(symbol|default|zero|singular|dual|paucal|plural|many|beforeCount|afterCount|now|today|never):.*\\{[0-9]+\\}' src/Humanizer/Locales/<code>.yml","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Literals are plain text; use countPlacement + forms or an explicit template for interpolation.","Do not paste string.Format patterns into literal fields.","Build after migrating resources."],"tags":["humanizer","source-generator","locale","yaml","localization","phrases","placeholder","literal"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}