From 13462bd8fd39a3a9dac2b2fa12f8d959203d7df6 Mon Sep 17 00:00:00 2001 From: David Hoff-Vanoni Date: Sat, 14 Sep 2024 01:06:01 -0700 Subject: [PATCH] feat: Localize sync progress window (#576) * refactor: Use `FluentMessageId` type for remaining l10n IDs * feat: Localize sync progress window * refactor: Allow parameterization of l10n types --- src/content/prefs/notero-pref.ts | 6 +++++- src/content/prefs/preferences.tsx | 3 ++- src/content/services/ui-manager.ts | 3 ++- src/content/sync/progress-window.ts | 24 +++++++++++++++++------- src/content/sync/sync-job.ts | 5 +++-- src/locale/en-US/notero.ftl | 5 +++++ types/l10n.d.ts | 26 +++++++++++++------------- 7 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/content/prefs/notero-pref.ts b/src/content/prefs/notero-pref.ts index dbd5466..c23e32a 100644 --- a/src/content/prefs/notero-pref.ts +++ b/src/content/prefs/notero-pref.ts @@ -1,3 +1,4 @@ +import { FluentMessageId } from '../../locale/fluent-types'; import { MissingPrefError } from '../errors'; export enum NoteroPref { @@ -18,7 +19,10 @@ export enum PageTitleFormat { itemTitle = 'itemTitle', } -export const PAGE_TITLE_FORMAT_L10N_IDS: Record = { +export const PAGE_TITLE_FORMAT_L10N_IDS: Record< + PageTitleFormat, + FluentMessageId +> = { [PageTitleFormat.itemAuthorDateCitation]: 'notero-page-title-format-item-author-date-citation', [PageTitleFormat.itemCitationKey]: diff --git a/src/content/prefs/preferences.tsx b/src/content/prefs/preferences.tsx index ea264de..8ad6f72 100644 --- a/src/content/prefs/preferences.tsx +++ b/src/content/prefs/preferences.tsx @@ -4,6 +4,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import type { createRoot } from 'react-dom/client'; +import { FluentMessageId } from '../../locale/fluent-types'; import { LocalizableError } from '../errors'; import { getNotionClient } from '../sync/notion-client'; import { @@ -26,7 +27,7 @@ type ReactDOMClient = typeof ReactDOM & { createRoot: typeof createRoot }; type MenuItem = { disabled?: boolean; - l10nId?: string; + l10nId?: FluentMessageId; label?: string; value: string; }; diff --git a/src/content/services/ui-manager.ts b/src/content/services/ui-manager.ts index aea0780..832d537 100644 --- a/src/content/services/ui-manager.ts +++ b/src/content/services/ui-manager.ts @@ -1,3 +1,4 @@ +import { FluentMessageId } from '../../locale/fluent-types'; import { createXULElement, logger } from '../utils'; import type { EventManager } from './event-manager'; @@ -103,7 +104,7 @@ export class UIManager implements Service { parentId, window, }: { - l10nId: string; + l10nId: FluentMessageId; onCommand: (event: Event) => void; parentId: string; window: Zotero.ZoteroWindow; diff --git a/src/content/sync/progress-window.ts b/src/content/sync/progress-window.ts index ce41120..ab1eb65 100644 --- a/src/content/sync/progress-window.ts +++ b/src/content/sync/progress-window.ts @@ -1,20 +1,30 @@ +import { FluentMessageId } from '../../locale/fluent-types'; + export class ProgressWindow { private readonly itemCount: number; - private readonly itemProgress: Zotero.ProgressWindow.ItemProgress; + private itemProgress!: Zotero.ProgressWindow.ItemProgress; + private readonly l10n: L10n.Localization; private readonly progressWindow: Zotero.ProgressWindow; - public constructor(itemCount: number) { + public constructor(itemCount: number, window: Window) { this.itemCount = itemCount; + this.l10n = window.document.l10n; + this.progressWindow = new Zotero.ProgressWindow({ window }); + } - this.progressWindow = new Zotero.ProgressWindow(); - this.progressWindow.changeHeadline('Syncing items to Notion…'); + public async show() { + const headline = await this.l10n.formatValue('notero-progress-headline'); + this.progressWindow.changeHeadline(headline || 'Syncing items to Notion…'); this.progressWindow.show(); - this.itemProgress = new this.progressWindow.ItemProgress('document', ''); } - public updateText(step: number) { - this.itemProgress.setText(`Item ${step} of ${this.itemCount}`); + public async updateText(step: number) { + const args = { step, total: this.itemCount }; + const message = + (await this.l10n.formatValue('notero-progress-item', args)) || + `Item ${step} of ${this.itemCount}`; + this.itemProgress.setText(message); } public updateProgress(step: number) { diff --git a/src/content/sync/sync-job.ts b/src/content/sync/sync-job.ts index 797a9a3..b912d04 100644 --- a/src/content/sync/sync-job.ts +++ b/src/content/sync/sync-job.ts @@ -43,7 +43,8 @@ export async function performSyncJob( const items = Zotero.Items.get(Array.from(itemIDs)); if (!items.length) return; - const progressWindow = new ProgressWindow(items.length); + const progressWindow = new ProgressWindow(items.length, window); + await progressWindow.show(); try { const syncJob = await prepareSyncJob({ items, progressWindow, window }); @@ -161,7 +162,7 @@ class SyncJob { ); logger.debug(item.getDisplayTitle()); - this.progressWindow.updateText(step); + await this.progressWindow.updateText(step); try { if (item.isNote()) { diff --git a/src/locale/en-US/notero.ftl b/src/locale/en-US/notero.ftl index 42457d7..5a1c553 100644 --- a/src/locale/en-US/notero.ftl +++ b/src/locale/en-US/notero.ftl @@ -52,6 +52,11 @@ notero-preferences-sync-on-modify-items = notero-preferences-sync-notes = .label = Sync notes +## Progress window + +notero-progress-headline = Syncing items to Notion… +notero-progress-item = Item { $step } of { $total } + ## Errors notero-error-missing-notion-database = Notion database not selected. Please select your database in Notero preferences. diff --git a/types/l10n.d.ts b/types/l10n.d.ts index af6a113..d72a6c6 100644 --- a/types/l10n.d.ts +++ b/types/l10n.d.ts @@ -7,12 +7,12 @@ declare namespace L10n { [key: string]: string | number; } - interface L10nIdArgs { - id: string | null; + interface L10nIdArgs { + id: I | null; args?: L10nArgs | null; } - type L10nKey = string | L10nIdArgs; + type L10nKey = I | L10nIdArgs; interface AttributeNameValue { name: string; @@ -27,7 +27,7 @@ declare namespace L10n { /** * @see https://searchfox.org/mozilla-esr115/source/dom/webidl/Localization.webidl */ - interface Localization { + interface Localization { ( resourceIds: L10nResourceId[], sync?: boolean, @@ -39,25 +39,25 @@ declare namespace L10n { removeResourceIds(resourceIds: L10nResourceId[]): number; - formatValue(id: string, args?: L10nArgs): Promise; + formatValue(id: I, args?: L10nArgs): Promise; - formatValues(keys: L10nKey[]): Promise<(string | null)[]>; + formatValues(keys: L10nKey[]): Promise<(string | null)[]>; - formatMessages(keys: L10nKey[]): Promise<(L10nMessage | null)[]>; + formatMessages(keys: L10nKey[]): Promise<(L10nMessage | null)[]>; setAsync(): void; - formatValueSync(id: string, args?: L10nArgs): string | null; + formatValueSync(id: I, args?: L10nArgs): string | null; - formatValuesSync(keys: L10nKey[]): (string | null)[]; + formatValuesSync(keys: L10nKey[]): (string | null)[]; - formatMessagesSync(keys: L10nKey[]): (L10nMessage | null)[]; + formatMessagesSync(keys: L10nKey[]): (L10nMessage | null)[]; } /** * @see https://searchfox.org/mozilla-esr115/source/dom/webidl/DOMLocalization.webidl */ - interface DOMLocalization extends Localization { + interface DOMLocalization extends Localization { connectRoot(element: Node): void; disconnectRoot(element: Node): void; @@ -66,9 +66,9 @@ declare namespace L10n { resumeObserving(): void; - setAttributes(element: Element, id: string, args?: object): void; + setAttributes(element: Element, id: I, args?: object): void; - getAttributes(element: Element): L10nIdArgs; + getAttributes(element: Element): L10nIdArgs; setArgs(element: Element, args?: object): void;