{"record":{"id":"0e8379dba721e259","repo":"halo-dev/halo","slug":"locale-cannot-be-used-as-it-does-not-specify","errorCode":null,"errorMessage":"Locale \"{}\" cannot be used as it does not specify a language.","messagePattern":"Locale \"(.+?)\" cannot be used as it does not specify a language\\.","errorType":"exception","errorClass":"TemplateProcessingException","httpStatus":null,"severity":"error","filePath":"application/src/main/java/run/halo/app/theme/message/ThemeMessageResolutionUtils.java","lineNumber":94,"sourceCode":"\n            } catch (final IOException ignored) {\n                // File might not exist, simply try the next one\n            }\n        }\n\n        if (combinedMessages == null) {\n            return EMPTY_MESSAGES;\n        }\n\n        return Collections.unmodifiableMap(combinedMessages);\n    }\n\n    private static List<String> computeMessageResourceNamesFromBase(final Locale locale) {\n\n        final List<String> resourceNames = new ArrayList<>(5);\n\n        if (StringUtils.isEmptyOrWhitespace(locale.getLanguage())) {\n            throw new TemplateProcessingException(\n                    \"Locale \\\"\" + locale + \"\\\" \" + \"cannot be used as it does not specify a language.\");\n        }\n\n        resourceNames.add(getResourceName(\"default\"));\n        resourceNames.add(getResourceName(locale.getLanguage()));\n\n        if (!StringUtils.isEmptyOrWhitespace(locale.getCountry())) {\n            resourceNames.add(getResourceName(locale.getLanguage() + \"_\" + locale.getCountry()));\n        }\n\n        if (!StringUtils.isEmptyOrWhitespace(locale.getVariant())) {\n            resourceNames.add(\n                    getResourceName(locale.getLanguage() + \"_\" + locale.getCountry() + \"-\" + locale.getVariant()));\n        }\n\n        return resourceNames;\n    }\n","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/theme/message/ThemeMessageResolutionUtils.java#L76-L112","documentation":"ThemeMessageResolutionUtils.computeMessageResourceNamesFromBase() builds the i18n resource name list for a theme and requires the Locale to carry a language. If locale.getLanguage() is empty/blank, a TemplateProcessingException is thrown because there is no base language to resolve message files (messages_<language>.properties) against. Thymeleaf rethrows this during theme rendering.","triggerScenarios":"Rendering a theme page when the resolved Locale (from site settings, Accept-Language, or a controller) is Locale.ROOT or otherwise has no language component. computeMessageResourceNamesFromBase is called while resolving theme message bundles for the current request locale.","commonSituations":"Site default locale misconfigured to an empty/blank value; a client sending an Accept-Language that resolves to a language-less Locale; a custom LocaleResolver returning Locale.ROOT; migration that wiped the locale setting.","solutions":["Set a concrete default site locale/language in Halo system settings so every request resolves a non-empty language.","Verify any custom LocaleResolver never returns Locale.ROOT for theme rendering.","Send a valid Accept-Language header from the client, or rely on the configured default.","If migrating, re-populate the locale setting before exposing the site."],"exampleFix":"// before: resolver can return Locale.ROOT\n//   Locale locale = resolver.resolveLocale(request); // may be ROOT\n// after: guarantee a language\n//   Locale locale = resolver.resolveLocale(request);\n//   if (locale == null || locale.getLanguage().isBlank()) {\n//       locale = Locale.getDefault();\n//   }","handlingStrategy":"validation","validationCode":"// Before rendering a theme, ensure the Locale has a language:\nLocale locale = localeResolver.resolveLocale(exchange.getRequest());\nif (locale == null || locale.getLanguage() == null || locale.getLanguage().isBlank()) {\n    locale = Locale.getDefault(); // or a configured fallback\n}","typeGuard":"// Java guard\nstatic boolean hasLanguage(Locale l) {\n    return l != null && !l.getLanguage().isBlank();\n}","tryCatchPattern":"try {\n    renderTheme(model, locale);\n} catch (TemplateProcessingException e) {\n    if (e.getMessage().contains(\"does not specify a language\")) {\n        renderTheme(model, Locale.getDefault()); // fallback locale\n    } else throw e;\n}","preventionTips":["Always configure a concrete default site locale in system settings.","Ensure any custom LocaleResolver never returns Locale.ROOT for theme pages.","Send a valid Accept-Language header from clients.","Add a test rendering a theme with the default locale to catch regressions."],"tags":["theme","i18n","locale","thymeleaf"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}