fix(ui): invalid language crashes app

- Apparently locales must use hyphens instead of underscores. This must
have been a fairly recent change that we didn't catch. It caused i18n to
throw for Brasilian Portuguese and both Simplified and Traditional
Mandarin. Change the locales to use the right strings.
- Move the theme + locale provider inside of the error boundary. This
allows errors with locals to be caught by the error boundary instead of
hard-crashing the app. The error screen is unstyled if this happens but
at least it has the reset button.
- Add a migration for the system slice to fix existing users' language
selections. For example, if the user had an incorrect language setting
of `zh_CN`, it will be changed to the correct `zh-CN`.
This commit is contained in:
psychedelicious
2025-07-10 14:00:30 +10:00
parent b1193022f7
commit 471c010217
5 changed files with 22 additions and 18 deletions

View File

@@ -26,14 +26,14 @@ const optionsObject: Record<Language, string> = {
nl: 'Nederlands',
pl: 'Polski',
pt: 'Português',
pt_BR: 'Português do Brasil',
'pt-BR': 'Português do Brasil',
ru: 'Русский',
sv: 'Svenska',
tr: 'Türkçe',
ua: 'Украї́нська',
vi: 'Tiếng Việt',
zh_CN: '简体中文',
zh_Hant: '漢語',
'zh-CN': '简体中文',
'zh-Hant': '漢語',
};
const options = map(optionsObject, (label, value) => ({ label, value }));

View File

@@ -9,7 +9,7 @@ import { uniq } from 'es-toolkit/compat';
import type { Language, SystemState } from './types';
const initialSystemState: SystemState = {
_version: 1,
_version: 2,
shouldConfirmOnDelete: true,
shouldAntialiasProgressImage: false,
shouldConfirmOnNewSession: true,
@@ -96,6 +96,10 @@ const migrateSystemState = (state: any): any => {
if (!('_version' in state)) {
state._version = 1;
}
if (state._version === 1) {
state.language = (state as SystemState).language.replace('_', '-');
state._version = 2;
}
return state;
};

View File

@@ -17,20 +17,20 @@ const zLanguage = z.enum([
'nl',
'pl',
'pt',
'pt_BR',
'pt-BR',
'ru',
'sv',
'tr',
'ua',
'vi',
'zh_CN',
'zh_Hant',
'zh-CN',
'zh-Hant',
]);
export type Language = z.infer<typeof zLanguage>;
export const isLanguage = (v: unknown): v is Language => zLanguage.safeParse(v).success;
export interface SystemState {
_version: 1;
_version: 2;
shouldConfirmOnDelete: boolean;
shouldAntialiasProgressImage: boolean;
shouldConfirmOnNewSession: boolean;