{"record":{"id":"d50d530f53a474cd","repo":"Humanizr/Humanizer","slug":"legacy-locale-localecode-defines-unsupported-t","errorCode":null,"errorMessage":"Legacy locale '{localeCode}' defines unsupported top-level property '{property}'.","messagePattern":"Legacy locale '(.+?)' defines unsupported top-level property '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/CanonicalLocaleAuthoring.cs","lineNumber":658,"sourceCode":"            \"dateOnlyToOrdinalWords\",\n            \"dateToOrdinalWords\",\n            \"durationCases\",\n            \"formatter\",\n            \"grammar\",\n            \"headings\",\n            \"numberToWords\",\n            \"ordinalizer\",\n            \"phrases\",\n            \"timeOnlyToClockNotation\",\n            \"wordsToNumber\"\n        ];\n\n        public static string ConvertToCanonicalYaml(string localeCode, string fileText)\n        {\n            var root = SimpleYamlParser.Parse(fileText);\n            foreach (var property in root.Values.Keys.Where(static property => !LegacyTopLevelNames.Contains(property, StringComparer.Ordinal)))\n            {\n                throw new InvalidOperationException(\n                    $\"Legacy locale '{localeCode}' defines unsupported top-level property '{property}'.\");\n            }\n\n            var builder = new StringBuilder();\n            builder.Append(\"locale: \");\n            builder.AppendLine(QuoteScalar(new SimpleYamlScalar(localeCode, true)));\n\n            if (root.GetScalar(\"inherits\") is { } variantOf)\n            {\n                builder.Append(\"variantOf: \");\n                builder.AppendLine(QuoteScalar(new SimpleYamlScalar(variantOf, true)));\n            }\n\n            var hasInherits = root.GetScalar(\"inherits\") is not null;\n            var hasSurfaces = root.Values.Keys.Any(static key => key != \"inherits\");\n            if (!hasSurfaces)\n            {\n                if (hasInherits)","sourceCodeStart":640,"sourceCodeEnd":676,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/CanonicalLocaleAuthoring.cs#L640-L676","documentation":"Thrown by LegacyLocaleMigration.ConvertToCanonicalYaml when a legacy locale file contains a top-level YAML property that is not in the LegacyTopLevelNames whitelist (inherits, collectionFormatter, dateOnlyToOrdinalWords, dateToOrdinalWords, durationCases, formatter, grammar, headings, numberToWords, ordinalizer, phrases, timeOnlyToClockNotation, wordsToNumber). The migration tool only knows how to translate these specific legacy properties into canonical surfaces.","triggerScenarios":"ConvertToCanonicalYaml parses the legacy file, iterates root.Values.Keys, and throws for any key not in LegacyTopLevelNames. For example, a legacy file with a top-level 'ordinal:' or 'clock:' key (which are canonical surface names, not legacy names) would fail.","commonSituations":"A contributor runs the legacy migration on a file that was already partially converted to canonical format, mixing canonical and legacy keys. Or a custom/typo property name is present at the root level. Also happens when a locale file uses an older schema with properties the migration tool doesn't recognize.","solutions":["Remove or rename the unsupported top-level property so only whitelisted legacy names remain.","If the property is already canonical (e.g. 'surfaces'), migrate the entire file to canonical format instead of using ConvertToCanonicalYaml.","Check the LegacyTopLevelNames array in CanonicalLocaleAuthoring.cs:636 for the exact list of accepted property names."],"exampleFix":"# before (legacy file with unsupported key)\nordinal:\n  engine: default\ninherits: en\n# after (remove non-legacy key; or convert fully to canonical)\ninherits: en","handlingStrategy":"validation","validationCode":"static readonly HashSet<string> LegacyTopLevelNames = new(StringComparer.Ordinal)\n{\n    \"inherits\", \"collectionFormatter\", \"dateOnlyToOrdinalWords\", \"dateToOrdinalWords\",\n    \"durationCases\", \"formatter\", \"grammar\", \"headings\", \"numberToWords\",\n    \"ordinalizer\", \"phrases\", \"timeOnlyToClockNotation\", \"wordsToNumber\"\n};\n\nstatic string? FindUnsupportedLegacyKey(SimpleYamlMapping root) =>\n    root.Values.Keys.FirstOrDefault(k => !LegacyTopLevelNames.Contains(k));","typeGuard":null,"tryCatchPattern":"// Wrap ConvertToCanonicalYaml for safe migration with diagnostics\ntry\n{\n    var canonical = LegacyLocaleMigration.ConvertToCanonicalYaml(localeCode, fileText);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"unsupported top-level property\"))\n{\n    logger.LogWarning(\"Locale {Locale} has unsupported legacy properties; skipping migration\", localeCode);\n    throw;\n}","preventionTips":["Only run ConvertToCanonicalYaml on genuinely legacy locale files, not partially-converted ones.","Check every root-level key against the LegacyTopLevelNames list before migrating.","Prefer authoring canonical YAML directly for new locales."],"tags":["source-generator","locale","yaml","legacy-migration","canonical-authoring"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}