{"record":{"id":"43c1d5ab902acd3f","repo":"actualbudget/actual","slug":"unknown-locale-language","errorCode":null,"errorMessage":"Unknown locale ${language}","messagePattern":"Unknown locale (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/i18n.ts","lineNumber":18,"sourceCode":"import { initReactI18next } from 'react-i18next';\n\nimport * as Platform from '@actual-app/core/shared/platform';\nimport i18n from 'i18next';\nimport resourcesToBackend from 'i18next-resources-to-backend';\n\nimport { languages } from './languages';\n\nexport const availableLanguages = Platform.isPlaywright\n  ? []\n  : Object.keys(languages).map(path => path.split('/')[2].split('.')[0]);\n\nconst isLanguageAvailable = (language: string) =>\n  Object.hasOwn(languages, `/locale/${language}.json`);\n\nconst loadLanguage = (language: string) => {\n  if (!isLanguageAvailable(language)) {\n    throw new Error(`Unknown locale ${language}`);\n  }\n  return languages[`/locale/${language}.json`]();\n};\n\nvoid i18n\n  .use(initReactI18next)\n  .use(resourcesToBackend(loadLanguage))\n  .init({\n    lng: 'en',\n\n    // allow keys to be phrases having `:`, `.`\n    nsSeparator: false,\n    keySeparator: false,\n    // do not load a fallback\n    fallbackLng: false,\n    interpolation: {\n      escapeValue: false,\n    },","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/i18n.ts#L1-L36","documentation":"loadLanguage checks isLanguageAvailable(language), which tests whether the bundled language map has a key /locale/<language>.json. If the requested language has no bundled locale file, it throws 'Unknown locale <language>'. The available locales are determined at build time by the languages import map in i18n.ts.","triggerScenarios":"Calling i18n.changeLanguage (or the loader path used by initReactI18next) with a locale code that is not in the languages map — e.g. 'en-gb', 'pt-BR' or a custom code with wrong casing when only 'en', 'es', etc. are bundled.","commonSituations":"Reading a language preference from the OS or storage that was never bundled; a user manually editing settings; adding a new locale to the UI list without adding its /locale/*.json file; case mismatches like 'EN' vs 'en'.","solutions":["Use one of the bundled locale codes listed in the languages object in packages/desktop-client/src/i18n.ts","Normalize the stored language code to lowercase before calling loadLanguage","If a new locale is needed, add its JSON file under the locale directory so Vite bundles it into the languages map","Catch the error and fall back to 'en' instead of failing startup"],"exampleFix":"// before\nawait i18n.changeLanguage(userLanguage); // 'en-US' not bundled\n// after\nconst available = Object.keys(languages).map(k => k.replace('/locale/', '').replace('.json', ''));\nconst lang = userLanguage.toLowerCase();\nawait i18n.changeLanguage(available.includes(lang) ? lang : 'en');","handlingStrategy":"validation","validationCode":"const bundledLocales = new Set(Object.keys(languages).map(k => k.replace('/locale/', '').replace(/\\.json$/, '')));\nconst resolveLanguage = (lang: string) => {\n  const l = lang.toLowerCase();\n  if (bundledLocales.has(l)) return l;\n  const base = l.split('-')[0];\n  return bundledLocales.has(base) ? base : 'en';\n};","typeGuard":"const isAvailableLocale = (lang: string): lang is keyof typeof languages =>\n  Object.hasOwn(languages, `/locale/${lang}.json`);","tryCatchPattern":"try {\n  await i18n.changeLanguage(userLanguage);\n} catch (err) {\n  if (err.message.startsWith('Unknown locale')) {\n    await i18n.changeLanguage('en');\n  } else throw err;\n}","preventionTips":["Normalize locale codes to lowercase and fall back to the base language ('pt-BR' -> 'pt')","Derive any language picker options from the languages map so only bundled locales are offered","When adding a locale, add the JSON file, the map entry, and a smoke test together","Persist only validated locale codes to settings storage"],"tags":["i18n","locale","configuration"],"backgroundTag":"unknown-locale","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}