{"record":{"id":"82fccfd6acaec33c","repo":"HMCL-dev/HMCL","slug":"localized-text-values-must-be-strings-field","errorCode":null,"errorMessage":"Localized text values must be strings: ${field}","messagePattern":"Localized text values must be strings: (.+?)","errorType":"validation","errorClass":"JsonParseException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java","lineNumber":216,"sourceCode":"        }\n        return requireNonBlank(primitive.getAsString(), fieldName);\n    }\n\n    /// Parses a localized text value.\n    static LocalizedText parseLocalizedText(JsonElement element, String field) {\n        if (element instanceof JsonPrimitive primitive && primitive.isString()) {\n            return LocalizedText.plain(requireNonBlank(primitive.getAsString(), field));\n        }\n        if (element instanceof JsonObject localizedObject) {\n            if (localizedObject.isEmpty()) {\n                throw new JsonParseException(\"Localized text field is empty: \" + field);\n            }\n\n            LinkedHashMap<String, String> localizedValues = new LinkedHashMap<>();\n            for (Map.Entry<String, JsonElement> entry : localizedObject.entrySet()) {\n                JsonElement value = entry.getValue();\n                if (!(value instanceof JsonPrimitive primitive) || !primitive.isString()) {\n                    throw new JsonParseException(\"Localized text values must be strings: \" + field);\n                }\n                localizedValues.put(\n                        requireNonBlank(entry.getKey(), field),\n                        requireNonBlank(primitive.getAsString(), field));\n            }\n            return new LocalizedText(localizedValues);\n        }\n        throw new JsonParseException(\"Theme-pack localized text must be a string or object: \" + field);\n    }\n\n    /// Returns a validated localized text value.\n    static LocalizedText requireLocalizedText(LocalizedText value, String field) {\n        Objects.requireNonNull(value);\n\n        JsonElement element = JsonUtils.GSON.toJsonTree(value, LocalizedText.class);\n        return parseLocalizedText(element, field);\n    }\n","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManifest.java#L198-L234","documentation":"Thrown by parseLocalizedText when a localized text object contains a value that is not a JSON string. Each locale key in the localized object must map to a string; numbers, booleans, nested objects, arrays, or null values are rejected, with the field name in the message.","triggerScenarios":"Deserializing a manifest where a localized object has a non-string value, e.g. \"name\": { \"en\": \"My Theme\", \"zh\": 123 } or \"description\": { \"en\": { \"text\": \"x\" } }.","commonSituations":"A translation tool exported counts or flags as numbers; an author accidentally nested locale maps; a script interpolated a numeric variable as the translation value.","solutions":["Quote every value in the localized object so all values are JSON strings.","Remove or fix non-string entries; do not nest objects inside localized text.","Add a lint step that checks localized maps for string-only values."],"exampleFix":"// before\n\"name\": { \"en\": \"My Theme\", \"zh\": 123 }\n// after\n\"name\": { \"en\": \"My Theme\", \"zh\": \"我的主题\" }","handlingStrategy":"validation","validationCode":"JsonObject name = manifestJson.getAsJsonObject(\"name\");\nfor (Map.Entry<String, JsonElement> e : name.entrySet()) {\n    if (!(e.getValue() instanceof JsonPrimitive p && p.isString())) {\n        throw new IllegalArgumentException(\"locale '\" + e.getKey() + \"' must map to a string\");\n    }\n}","typeGuard":"static boolean isStringValuedMap(JsonObject localized) {\n    for (JsonElement v : localized.values()) {\n        if (!(v instanceof JsonPrimitive p) || !p.isString()) return false;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    ThemePackManifest pack = gson.fromJson(json, ThemePackManifest.class);\n} catch (JsonParseException e) {\n    // highlight the localized field named in the message\n}","preventionTips":["Configure translation tools to export all values as strings","Avoid nesting objects inside localized text maps","Lint localized maps for string-only values in CI"],"tags":["json","i18n","manifest","type-mismatch"],"backgroundTag":"config-type-mismatch","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}