Complete client UI i18n migration
Adds client-side i18n support, language settings, translated UI copy, and locale parity coverage.
This commit is contained in:
@@ -2,6 +2,7 @@ import { useCallback } from "react";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { useQuery, useQueryClient, type QueryClient } from "@tanstack/react-query";
|
||||
import { queryClient as appQueryClient } from "@/query/query-client";
|
||||
import type { AppLanguage } from "@/i18n/locales";
|
||||
import {
|
||||
DEFAULT_DESKTOP_SETTINGS,
|
||||
loadDesktopSettings,
|
||||
@@ -58,6 +59,7 @@ export {
|
||||
};
|
||||
export type {
|
||||
AppSettings,
|
||||
AppLanguage,
|
||||
DesktopSettingsBridge,
|
||||
KeyValueStorage,
|
||||
ReleaseChannel,
|
||||
@@ -143,6 +145,9 @@ export function useSettings(): UseSettingsReturn {
|
||||
if (updates.theme !== undefined) {
|
||||
appUpdates.theme = updates.theme;
|
||||
}
|
||||
if (updates.language !== undefined) {
|
||||
appUpdates.language = updates.language;
|
||||
}
|
||||
if (updates.sendBehavior !== undefined) {
|
||||
appUpdates.sendBehavior = updates.sendBehavior;
|
||||
}
|
||||
|
||||
@@ -47,11 +47,20 @@ describe("loadAppSettingsFromStorage", () => {
|
||||
const result = await loadAppSettingsFromStorage(deps);
|
||||
|
||||
expect(result).toEqual(DEFAULT_CLIENT_SETTINGS);
|
||||
expect(DEFAULT_CLIENT_SETTINGS.language).toBe("system");
|
||||
expect(deps.storage.entries.get(APP_SETTINGS_KEY)).toBe(
|
||||
JSON.stringify(DEFAULT_CLIENT_SETTINGS),
|
||||
);
|
||||
});
|
||||
|
||||
it("defaults language to system when storage is empty", async () => {
|
||||
const deps = makeDeps();
|
||||
|
||||
const result = await loadAppSettingsFromStorage(deps);
|
||||
|
||||
expect(result.language).toBe("system");
|
||||
});
|
||||
|
||||
it("loads configured terminal scrollback lines from app settings", async () => {
|
||||
const deps = makeDeps({
|
||||
storage: createInMemoryKeyValueStorage({
|
||||
@@ -95,6 +104,30 @@ describe("loadAppSettingsFromStorage", () => {
|
||||
});
|
||||
expect(deps.storage.entries.get(APP_SETTINGS_KEY)).toBe(JSON.stringify(result));
|
||||
});
|
||||
|
||||
it("loads a persisted explicit language", async () => {
|
||||
const deps = makeDeps({
|
||||
storage: createInMemoryKeyValueStorage({
|
||||
[APP_SETTINGS_KEY]: JSON.stringify({ language: "zh-CN" }),
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await loadAppSettingsFromStorage(deps);
|
||||
|
||||
expect(result.language).toBe("zh-CN");
|
||||
});
|
||||
|
||||
it("drops an unknown persisted language back to system", async () => {
|
||||
const deps = makeDeps({
|
||||
storage: createInMemoryKeyValueStorage({
|
||||
[APP_SETTINGS_KEY]: JSON.stringify({ language: "klingon" }),
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await loadAppSettingsFromStorage(deps);
|
||||
|
||||
expect(result.language).toBe("system");
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadSettingsFromStorage", () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { isSyntaxThemeId, type SyntaxThemeId } from "@getpaseo/highlight";
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import type { DesktopSettings } from "@/desktop/settings/desktop-settings";
|
||||
import { parseAppLanguage, type AppLanguage } from "@/i18n/locales";
|
||||
import { THEME_TO_UNISTYLES, type ThemeName } from "@/styles/theme";
|
||||
|
||||
export const APP_SETTINGS_KEY = "@paseo:app-settings";
|
||||
@@ -26,6 +27,7 @@ export const MAX_FONT_FAMILY_LENGTH = 200;
|
||||
|
||||
export interface AppSettings {
|
||||
theme: ThemeName | "auto";
|
||||
language: AppLanguage;
|
||||
sendBehavior: SendBehavior;
|
||||
serviceUrlBehavior: ServiceUrlBehavior;
|
||||
terminalScrollbackLines: number;
|
||||
@@ -43,6 +45,7 @@ export interface Settings extends AppSettings {
|
||||
|
||||
export const DEFAULT_CLIENT_SETTINGS: AppSettings = {
|
||||
theme: "auto",
|
||||
language: "system",
|
||||
sendBehavior: "interrupt",
|
||||
serviceUrlBehavior: "ask",
|
||||
terminalScrollbackLines: DEFAULT_TERMINAL_SCROLLBACK_LINES,
|
||||
@@ -149,6 +152,10 @@ function pickAppSettings(stored: Partial<AppSettings>): Partial<AppSettings> {
|
||||
if (typeof stored.theme === "string" && VALID_THEMES.has(stored.theme)) {
|
||||
result.theme = stored.theme;
|
||||
}
|
||||
const language = parseAppLanguage(stored.language);
|
||||
if (language !== null) {
|
||||
result.language = language;
|
||||
}
|
||||
if (stored.sendBehavior === "interrupt" || stored.sendBehavior === "queue") {
|
||||
result.sendBehavior = stored.sendBehavior;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user