{"record":{"id":"7f4967d892c04a1b","repo":"angular/angular","slug":"required-translations-property-missing","errorCode":null,"errorMessage":"Required \"translations\" property missing.","messagePattern":"Required \"translations\" property missing\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/localize/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts","lineNumber":59,"sourceCode":"    if (\n      extname(filePath) !== '.json' ||\n      !(contents.includes('\"locale\"') && contents.includes('\"translations\"'))\n    ) {\n      diagnostics.warn('File does not have .json extension.');\n      return {canParse: false, diagnostics};\n    }\n    try {\n      const json = JSON.parse(contents) as SimpleJsonFile;\n      if (json.locale === undefined) {\n        diagnostics.warn('Required \"locale\" property missing.');\n        return {canParse: false, diagnostics};\n      }\n      if (typeof json.locale !== 'string') {\n        diagnostics.warn('The \"locale\" property is not a string.');\n        return {canParse: false, diagnostics};\n      }\n      if (json.translations === undefined) {\n        diagnostics.warn('Required \"translations\" property missing.');\n        return {canParse: false, diagnostics};\n      }\n      if (typeof json.translations !== 'object') {\n        diagnostics.warn('The \"translations\" is not an object.');\n        return {canParse: false, diagnostics};\n      }\n      return {canParse: true, diagnostics, hint: json};\n    } catch (e) {\n      diagnostics.warn('File is not valid JSON.');\n      return {canParse: false, diagnostics};\n    }\n  }\n\n  parse(_filePath: string, contents: string, json?: SimpleJsonFile): ParsedTranslationBundle {\n    const {locale: parsedLocale, translations} = json || (JSON.parse(contents) as SimpleJsonFile);\n    const parsedTranslations: Record<MessageId, ɵParsedTranslation> = {};\n    for (const messageId in translations) {\n      const targetMessage = translations[messageId];","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/angular/angular/blob/51cb07e98081ab7e4e84a9e0949266a8e19cca84/packages/localize/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.ts#L41-L77","documentation":"The parsed JSON object is missing the `translations` property. Because the earlier precheck only looked for the substring `\"translations\"` in the raw text, the word can appear (nested or in a value) while the required top-level key is absent, which is exactly what triggers this warning and a canParse=false verdict.","triggerScenarios":"A `.json` file with a top-level `locale` but translations nested under another key, e.g. `{ \"locale\": \"fr\", \"messages\": { ... } }` - the substring `\"translations\"` may occur in a comment-ish value while the key itself is absent.","commonSituations":"Generators that name the map `strings`, `messages`, or `dict` instead of `translations`; partial hand-written files.","solutions":["Rename the message map key to `translations` so it sits at the top level.","If the file intentionally uses another key, transform it when generating the file rather than renaming the extension.","Validate the schema (`locale` string + `translations` object at top level) before invoking the translate step."],"exampleFix":"// before\n{ \"locale\": \"fr\", \"messages\": { \"hello\": \"Bonjour\" } }\n\n// after\n{ \"locale\": \"fr\", \"translations\": { \"hello\": \"Bonjour\" } }","handlingStrategy":"type-guard","validationCode":"const json: unknown = JSON.parse(readFileSync(path, 'utf8'));\nif (!isSimpleJsonFile(json)) throw new Error(`${path}: top-level 'translations' object is required`);","typeGuard":"function hasTranslations(v: unknown): v is {translations: Record<string, unknown>} {\n  return typeof v === 'object' && v !== null && 'translations' in v\n    && typeof (v as {translations: unknown}).translations === 'object';\n}","tryCatchPattern":null,"preventionTips":["Standardize on the key name `translations` in every generated file.","Add a schema assertion so renamed keys (messages/strings/dict) fail fast at generation time."],"tags":["localize","i18n","json","translations","schema","translation-file"],"backgroundTag":"translation-file-schema-invalid","analyzedSha":"51cb07e98081ab7e4e84a9e0949266a8e19cca84","analyzedAt":"2026-08-22T07:04:54.531Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}