mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-28 15:32:59 +00:00
Keep translation details out of startup path
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { getLanguage, requireApiVersion } from "@/deps";
|
||||
import { getLanguage, Notice, requireApiVersion } from "@/deps";
|
||||
import { createServiceFeature } from "@vrtmrz/livesync-commonlib/compat/interfaces/ServiceModule";
|
||||
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "@/common/rosetta";
|
||||
import { $msg, __onMissingTranslation, setLang } from "@/common/translation";
|
||||
@@ -15,7 +15,40 @@ function tryGetLanguage(onError: (error: unknown) => void) {
|
||||
return "en";
|
||||
}
|
||||
|
||||
export const enableI18nFeature = createServiceFeature(async ({ services: { setting, API } }) => {
|
||||
class ObsidianLanguageAppliedNotice {
|
||||
private reminder: Notice | undefined;
|
||||
|
||||
show(openDetails: () => void): void {
|
||||
this.clear();
|
||||
let reminderAnchor: HTMLAnchorElement | undefined;
|
||||
const appliedMessage =
|
||||
$msg("dialog.yourLanguageAvailable")
|
||||
.split(/\r?\n\s*\r?\n/u, 1)[0]
|
||||
?.trim() ?? $msg("Display Language");
|
||||
const fragment = createFragment((documentFragment) => {
|
||||
documentFragment.createSpan({
|
||||
text: `${appliedMessage} `,
|
||||
});
|
||||
documentFragment.createEl("a", { text: $msg("Open the dialog") }, (anchor) => {
|
||||
reminderAnchor = anchor;
|
||||
anchor.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
this.clear();
|
||||
openDetails();
|
||||
});
|
||||
});
|
||||
});
|
||||
this.reminder = new Notice(fragment, 0);
|
||||
reminderAnchor?.closest<HTMLElement>(".notice")?.classList.add("livesync-language-applied-notice");
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.reminder?.hide();
|
||||
this.reminder = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const enableI18nFeature = createServiceFeature(async ({ services: { setting, API, appLifecycle } }) => {
|
||||
// Clear missing translation handler to avoid unnecessary warnings.
|
||||
__onMissingTranslation(() => {});
|
||||
let isChanged = false;
|
||||
@@ -36,26 +69,48 @@ export const enableI18nFeature = createServiceFeature(async ({ services: { setti
|
||||
// settings.displayLanguage = obsidianLanguage as I18N_LANGS;
|
||||
await setting.applyPartial({ displayLanguage: obsidianLanguage as I18N_LANGS });
|
||||
isChanged = true;
|
||||
setLang(settings.displayLanguage);
|
||||
setLang(obsidianLanguage as I18N_LANGS);
|
||||
} else if (settings.displayLanguage == "") {
|
||||
// settings.displayLanguage = "def";
|
||||
await setting.applyPartial({ displayLanguage: "def" });
|
||||
setLang(settings.displayLanguage);
|
||||
setLang("def");
|
||||
await setting.saveSettingData();
|
||||
}
|
||||
}
|
||||
if (isChanged) {
|
||||
const revert = $msg("dialog.yourLanguageAvailable.btnRevertToDefault");
|
||||
if (
|
||||
(await API.confirm.askSelectStringDialogue($msg(`dialog.yourLanguageAvailable`), ["OK", revert], {
|
||||
defaultAction: "OK",
|
||||
title: $msg(`dialog.yourLanguageAvailable.Title`),
|
||||
})) == revert
|
||||
) {
|
||||
await setting.applyPartial({ displayLanguage: "def" });
|
||||
setLang(settings.displayLanguage);
|
||||
}
|
||||
await setting.saveSettingData();
|
||||
const reminder = new ObsidianLanguageAppliedNotice();
|
||||
appLifecycle.onUnload.addHandler(() => {
|
||||
reminder.clear();
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
reminder.show(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const revert = $msg("dialog.yourLanguageAvailable.btnRevertToDefault");
|
||||
if (
|
||||
(await API.confirm.askSelectStringDialogue(
|
||||
$msg(`dialog.yourLanguageAvailable`),
|
||||
["OK", revert],
|
||||
{
|
||||
defaultAction: "OK",
|
||||
title: $msg("Display Language"),
|
||||
}
|
||||
)) == revert
|
||||
) {
|
||||
await setting.applyPartial({ displayLanguage: "def" });
|
||||
setLang("def");
|
||||
await setting.saveSettingData();
|
||||
}
|
||||
} catch (error) {
|
||||
API.addLog(
|
||||
`Failed to open translation details: ${String(error)}`,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
"i18n-language"
|
||||
);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const noticeState = vi.hoisted(() => ({
|
||||
instances: [] as Array<{ hide: ReturnType<typeof vi.fn>; duration: number }>,
|
||||
spanTexts: [] as string[],
|
||||
}));
|
||||
|
||||
vi.mock("@/deps", () => ({
|
||||
getLanguage: () => "ja",
|
||||
requireApiVersion: () => true,
|
||||
Notice: class {
|
||||
hide = vi.fn();
|
||||
|
||||
constructor(_fragment: unknown, duration: number) {
|
||||
noticeState.instances.push({ hide: this.hide, duration });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/common/translation", () => ({
|
||||
$msg: (key: string) =>
|
||||
({
|
||||
"dialog.yourLanguageAvailable": "Translation has been applied.\n\nMore details.",
|
||||
"dialog.yourLanguageAvailable.btnRevertToDefault": "Keep Default",
|
||||
"dialog.yourLanguageAvailable.Title": "Translation is available!",
|
||||
"Display Language": "Display language",
|
||||
"Open the dialog": "Open the dialogue",
|
||||
})[key] ?? key,
|
||||
__onMissingTranslation: vi.fn(),
|
||||
setLang: vi.fn(),
|
||||
}));
|
||||
|
||||
import { enableI18nFeature } from "./enablei18n.ts";
|
||||
|
||||
describe("automatic display language", () => {
|
||||
let clickDetails: ((event: { preventDefault(): void }) => void) | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
noticeState.instances.length = 0;
|
||||
noticeState.spanTexts.length = 0;
|
||||
clickDetails = undefined;
|
||||
vi.stubGlobal("createFragment", (build: (fragment: unknown) => void) => {
|
||||
const anchor = {
|
||||
addEventListener: (_event: string, listener: (event: { preventDefault(): void }) => void) => {
|
||||
clickDetails = listener;
|
||||
},
|
||||
closest: () => ({ classList: { add: vi.fn() } }),
|
||||
};
|
||||
const fragment = {
|
||||
createSpan: ({ text }: { text: string }) => noticeState.spanTexts.push(text),
|
||||
createEl: (_tag: string, _options: unknown, configure: (element: typeof anchor) => void) => {
|
||||
configure(anchor);
|
||||
return anchor;
|
||||
},
|
||||
};
|
||||
build(fragment);
|
||||
return fragment;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("lets start-up continue and opens translation details only from a persistent Notice", async () => {
|
||||
const settings = { displayLanguage: "" };
|
||||
const applyPartial = vi.fn(async (partial: Partial<typeof settings>) => Object.assign(settings, partial));
|
||||
const saveSettingData = vi.fn().mockResolvedValue(undefined);
|
||||
const askSelectStringDialogue = vi.fn().mockResolvedValue("Keep Default");
|
||||
const unloadHandlers: Array<() => Promise<boolean>> = [];
|
||||
const host = {
|
||||
services: {
|
||||
setting: {
|
||||
currentSettings: () => settings,
|
||||
applyPartial,
|
||||
saveSettingData,
|
||||
},
|
||||
API: {
|
||||
addLog: vi.fn(),
|
||||
confirm: { askSelectStringDialogue },
|
||||
},
|
||||
appLifecycle: {
|
||||
onUnload: {
|
||||
addHandler: (handler: () => Promise<boolean>) => unloadHandlers.push(handler),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await expect(enableI18nFeature(host as never)).resolves.toBe(true);
|
||||
|
||||
expect(settings.displayLanguage).toBe("ja");
|
||||
expect(saveSettingData).toHaveBeenCalledOnce();
|
||||
expect(askSelectStringDialogue).not.toHaveBeenCalled();
|
||||
expect(noticeState.instances).toHaveLength(1);
|
||||
expect(noticeState.instances[0]?.duration).toBe(0);
|
||||
expect(noticeState.spanTexts).toEqual(["Translation has been applied. "]);
|
||||
expect(clickDetails).toBeTypeOf("function");
|
||||
|
||||
clickDetails?.({ preventDefault: vi.fn() });
|
||||
await vi.waitFor(() => expect(askSelectStringDialogue).toHaveBeenCalledOnce());
|
||||
expect(askSelectStringDialogue.mock.calls[0]?.[2]).toMatchObject({ title: "Display language" });
|
||||
await vi.waitFor(() => expect(settings.displayLanguage).toBe("def"));
|
||||
expect(saveSettingData).toHaveBeenCalledTimes(2);
|
||||
|
||||
await expect(unloadHandlers[0]?.()).resolves.toBe(true);
|
||||
expect(noticeState.instances[0]?.hide).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user