{"record":{"id":"a2d9ed867748286d","repo":"Humanizr/Humanizer","slug":"billion-word-cardinal-strategy-requires-a-singular","errorCode":null,"errorMessage":"Billion-word cardinal strategy requires a singular billion word.","messagePattern":"Billion-word cardinal strategy requires a singular billion word\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/GenerationHelpers.cs","lineNumber":736,"sourceCode":"\n            builder.Append(')');\n            first = false;\n        }\n\n        builder.Append(\" }\");\n        return builder.ToString();\n    }\n\n    static string CreateLegacyBillionStrategyCardinalScaleArrayExpression(JsonElement cardinalElement)\n    {\n        var millionScale = $\"new(1000000UL, {QuoteLiteral(GetRequiredString(cardinalElement, \"millionSingularWord\"))}, {QuoteLiteral(GetRequiredString(cardinalElement, \"millionPluralWord\"))})\";\n        if (GetRequiredString(cardinalElement, \"billionStrategy\") != \"billion-word\")\n        {\n            return $\"new BillionStrategyCardinalScale[] {{ {millionScale} }}\";\n        }\n\n        var billionSingular = GetOptionalString(cardinalElement, \"billionSingularWord\")\n            ?? throw new InvalidOperationException(\"Billion-word cardinal strategy requires a singular billion word.\");\n        var billionPlural = GetOptionalString(cardinalElement, \"billionPluralWord\")\n            ?? throw new InvalidOperationException(\"Billion-word cardinal strategy requires a plural billion word.\");\n        return $\"new BillionStrategyCardinalScale[] {{ new(1000000000UL, {QuoteLiteral(billionSingular)}, {QuoteLiteral(billionPlural)}), {millionScale} }}\";\n    }\n\n    static string CreateContractedOneScaleArrayExpression(JsonElement arrayElement)\n    {\n        if (arrayElement.ValueKind != JsonValueKind.Array)\n        {\n            throw new InvalidOperationException(\"Expected JSON array.\");\n        }\n\n        var builder = new StringBuilder(\"new ContractedOneScaleNumberToWordsConverter.Scale[] { \");\n        var first = true;\n\n        foreach (var item in arrayElement.EnumerateArray())\n        {\n            if (item.ValueKind != JsonValueKind.Object)","sourceCodeStart":718,"sourceCodeEnd":754,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/GenerationHelpers.cs#L718-L754","documentation":"Thrown when a locale's cardinal scale declares billionStrategy: 'billion-word' but omits the billionSingularWord property. The generator builds a BillionStrategyCardinalScale[] that needs an explicit singular billion term; without it the generated converter would lack a word for 1,000,000,000. It is a schema-completeness check keyed off the strategy value.","triggerScenarios":"A locale YAML sets numberToWords cardinal.billionStrategy to 'billion-word' (vs 'thousand-millions' or 'thousandth-millionth') but does not supply a parallel billionSingularWord string. The check runs in CreateLegacyBillionStrategyCardinalScaleArrayExpression during code generation.","commonSituations":"A contributor copies an existing billion-word locale (e.g. en, fr) and deletes or renames the billionSingularWord key, or switches billionStrategy from another value to 'billion-word' without adding the singular/plural word pair. Surfaces as a generator failure during dotnet build.","solutions":["In the offending locale YAML under numberToWords cardinal, add a 'billionSingularWord' key with the singular billion term (e.g. billionSingularWord: 'un milliard').","If the locale uses the thousand-millions scale instead, set billionStrategy to 'thousand-millions' so no billion word is required.","Cross-check against a known billion-word locale (en.yml, fr.yml) for the exact key names and placement."],"exampleFix":"# before\ncardinal:\n  billionStrategy: 'billion-word'\n  millionSingularWord: 'milhão'\n# after\ncardinal:\n  billionStrategy: 'billion-word'\n  billionSingularWord: 'mil milhões'\n  billionPluralWord: 'mil milhões'\n  millionSingularWord: 'milhão'","handlingStrategy":"validation","validationCode":"# In the locale YAML, before setting billionStrategy: 'billion-word',\n# confirm both keys exist. Quick check with ripgrep:\n#   rg -A2 \"billionStrategy: 'billion-word'\" src/Humanizer/Locales/<code>.yml\n# Ensure 'billionSingularWord' appears in the same cardinal block.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When switching billionStrategy to 'billion-word', add billionSingularWord and billionPluralWord in the same commit.","Mirror the en.yml or fr.yml cardinal block for exact key names and placement.","Build locally after locale changes so the generator validates the strategy/word pair."],"tags":["source-generator","locale-yaml","configuration","build-time","cardinal-scale","humanizer"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}