diff --git a/src/gui/ChoiceBuilder/captureChoiceBuilder.ts b/src/gui/ChoiceBuilder/captureChoiceBuilder.ts index c11f750c..51ade8ff 100644 --- a/src/gui/ChoiceBuilder/captureChoiceBuilder.ts +++ b/src/gui/ChoiceBuilder/captureChoiceBuilder.ts @@ -18,6 +18,7 @@ import { import { createValidatedInput } from "../components/validatedInput"; import { FormatSyntaxSuggester } from "../suggesters/formatSyntaxSuggester"; import { ChoiceBuilder } from "./choiceBuilder"; +import { t } from "../../i18n/i18n"; export class CaptureChoiceBuilder extends ChoiceBuilder { choice: ICaptureChoice; @@ -40,7 +41,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.addCenteredChoiceNameHeader(this.choice); // Location - new Setting(this.contentEl).setName("Location").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.location")).setHeading(); this.addCapturedToSetting(); if (!this.choice?.captureToActiveFile) { this.addCreateIfNotExistsSetting(); @@ -49,22 +50,22 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { } // Position - new Setting(this.contentEl).setName("Position").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.position")).setHeading(); this.addWritePositionSetting(); // Linking - new Setting(this.contentEl).setName("Linking").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.linking")).setHeading(); this.addAppendLinkSetting(); // Content - new Setting(this.contentEl).setName("Content").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.content")).setHeading(); this.addTaskSetting(); this.addFormatSetting(); // Behavior - new Setting(this.contentEl).setName("Behavior").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.behavior")).setHeading(); if (!this.choice.captureToActiveFile) { - this.addOpenFileSetting("Open the captured file."); + this.addOpenFileSetting(t("builder.capture.open_file_desc")); if (this.choice.openFile) { this.addFileOpeningSetting("captured"); @@ -77,10 +78,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { private addTemplaterAfterCaptureSetting() { new Setting(this.contentEl) - .setName("Run Templater on entire destination file after capture") - .setDesc( - "Advanced / legacy: this executes any `<% %>` anywhere in the destination file (including inside code blocks).", - ) + .setName(t("builder.capture.templater_after_capture")) + .setDesc(t("builder.capture.templater_after_capture_desc")) .addToggle((toggle) => { toggle.setValue(this.choice.templater?.afterCapture === "wholeFile"); toggle.onChange((value) => { @@ -94,15 +93,13 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { private addSelectionAsValueSetting() { new Setting(this.contentEl) - .setName("Use editor selection as default value") - .setDesc( - "Controls whether this Capture uses the current editor selection as {{VALUE}}. Does not affect {{SELECTED}}.", - ) + .setName(t("settings.selection_capture.name")) + .setDesc(t("settings.selection_capture.desc")) .addDropdown((dropdown) => { dropdown.addOptions({ - "": "Follow global setting", - enabled: "Use selection", - disabled: "Ignore selection", + "": t("builder.one_page_override.follow"), + enabled: t("builder.capture.selection_options.use"), + disabled: t("builder.capture.selection_options.ignore"), }); const override = this.choice.useSelectionAsCaptureValue; dropdown.setValue( @@ -124,8 +121,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { private addCapturedToSetting() { new Setting(this.contentEl) - .setName("Capture to") - .setDesc("Target file path. Supports format syntax."); + .setName(t("builder.capture.to")) + .setDesc(t("builder.capture.to_desc")); const captureToContainer: HTMLDivElement = this.contentEl.createDiv("captureToContainer"); @@ -134,7 +131,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { captureToContainer.createDiv("captureToActiveFileContainer"); const captureToActiveFileText: HTMLSpanElement = captureToActiveFileContainer.createEl("span"); - captureToActiveFileText.textContent = "Capture to active file"; + captureToActiveFileText.textContent = t("builder.capture.active_file"); const captureToActiveFileToggle: ToggleComponent = new ToggleComponent( captureToActiveFileContainer, ); @@ -143,7 +140,6 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.choice.captureToActiveFile = value; // Reset new line capture settings when switching away from active file - // since those options are only available for active file capture if (!value && this.choice.newLineCapture?.enabled) { this.choice.newLineCapture.enabled = false; } @@ -156,17 +152,17 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { captureToContainer.createDiv("captureToFileContainer"); new Setting(captureToFileContainer) - .setName("File path / format") - .setDesc("Choose a file or use format syntax (e.g., {{DATE}})"); + .setName(t("builder.capture.file_path")) + .setDesc(t("builder.capture.file_path_desc")); const displayFormatter: FileNameDisplayFormatter = new FileNameDisplayFormatter(this.app, this.plugin); const previewRow = captureToFileContainer.createDiv({ cls: "qa-preview-row" }); - previewRow.createEl("span", { text: "Preview: ", cls: "qa-preview-label" }); + previewRow.createEl("span", { text: t("builder.common.preview"), cls: "qa-preview-label" }); const formatDisplay = previewRow.createEl("span"); formatDisplay.setAttr("aria-live", "polite"); - formatDisplay.textContent = "Loading preview…"; + formatDisplay.textContent = t("builder.common.loading"); const markdownFilesAndFormatSyntax = [ ...this.app.vault.getMarkdownFiles().map((f) => f.path), @@ -177,7 +173,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { app: this.app, parent: captureToFileContainer, initialValue: this.choice.captureTo, - placeholder: "File name format", + placeholder: t("builder.capture.file_name_format"), suggestions: markdownFilesAndFormatSyntax, maxSuggestions: 50, attachSuggesters: [ @@ -188,7 +184,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { try { formatDisplay.textContent = await displayFormatter.format(value); } catch { - formatDisplay.textContent = "Preview unavailable"; + formatDisplay.textContent = t("builder.common.unavailable"); } }, }); @@ -199,7 +195,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.choice.captureTo, ); } catch { - formatDisplay.textContent = "Preview unavailable"; + formatDisplay.textContent = t("builder.common.unavailable"); } })(); } @@ -208,8 +204,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { private addTaskSetting() { const taskSetting: Setting = new Setting(this.contentEl); taskSetting - .setName("Task") - .setDesc("Formats the value as a task.") + .setName(t("builder.capture.task")) + .setDesc(t("builder.capture.task_desc")) .addToggle((toggle) => { toggle.setValue(this.choice.task); toggle.onChange((value) => (this.choice.task = value)); @@ -232,12 +228,12 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { const appendLinkSetting: Setting = new Setting(this.contentEl); appendLinkSetting - .setName("Link to captured file") - .setDesc("Choose how QuickAdd should insert a link to the captured file in the current note.") + .setName(t("builder.append_link.name")) + .setDesc(t("builder.append_link.desc")) .addDropdown((dropdown) => { - dropdown.addOption("required", "Enabled (requires active file)"); - dropdown.addOption("optional", "Enabled (skip if no active file)"); - dropdown.addOption("disabled", "Disabled"); + dropdown.addOption("required", t("builder.append_link.options.required")); + dropdown.addOption("optional", t("builder.append_link.options.optional")); + dropdown.addOption("disabled", t("builder.append_link.options.disabled")); dropdown.setValue(currentMode); dropdown.onChange((value: AppendLinkMode) => { @@ -270,13 +266,13 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { if (currentMode !== "disabled") { const placementSetting: Setting = new Setting(this.contentEl); placementSetting - .setName("Link placement") - .setDesc("Where to place the link when appending") + .setName(t("builder.append_link.placement")) + .setDesc(t("builder.append_link.placement_desc")) .addDropdown((dropdown) => { - dropdown.addOption("replaceSelection", "Replace selection"); - dropdown.addOption("afterSelection", "After selection"); - dropdown.addOption("endOfLine", "End of line"); - dropdown.addOption("newLine", "New line"); + dropdown.addOption("replaceSelection", t("builder.append_link.options.replace")); + dropdown.addOption("afterSelection", t("builder.append_link.options.after")); + dropdown.addOption("endOfLine", t("builder.append_link.options.eol")); + dropdown.addOption("newLine", t("builder.append_link.options.newline")); dropdown.setValue(normalizedOptions.placement); dropdown.onChange((value: LinkPlacement) => { @@ -307,11 +303,11 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { if (placementSupportsEmbed(normalizedOptions.placement)) { const linkTypeSetting: Setting = new Setting(this.contentEl); linkTypeSetting - .setName("Link type") - .setDesc("Choose whether to insert a regular link or an embed when replacing the selection.") + .setName(t("builder.append_link.type")) + .setDesc(t("builder.append_link.type_desc")) .addDropdown((dropdown) => { - dropdown.addOption("link", "Link"); - dropdown.addOption("embed", "Embed"); + dropdown.addOption("link", t("builder.append_link.options.link")); + dropdown.addOption("embed", t("builder.append_link.options.embed")); dropdown.setValue(normalizedLinkType); dropdown.onChange((value: LinkType) => { const currentValue = this.choice.appendLink; @@ -345,12 +341,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { } positionSetting - .setName("Write position") - .setDesc( - isActiveFile - ? "Where to place the capture in the current file." - : "Where to place the capture in the target file.", - ) + .setName(t("builder.capture.write_pos")) + .setDesc(t("builder.capture.write_pos_desc")) .addDropdown((dropdown) => { const current = this.choice.insertAfter?.enabled @@ -365,16 +357,16 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { ? "activeTop" : "top"; - dropdown.addOption("top", isActiveFile ? "At cursor" : "Top of file"); + dropdown.addOption("top", isActiveFile ? t("builder.capture.pos_options.cursor") : t("builder.capture.pos_options.top")); if (isActiveFile) { - dropdown.addOption("activeTop", "Top of file (after frontmatter)"); - dropdown.addOption("newLineAbove", "New line above cursor"); - dropdown.addOption("newLineBelow", "New line below cursor"); + dropdown.addOption("activeTop", t("builder.capture.pos_options.active_top")); + dropdown.addOption("newLineAbove", t("builder.capture.pos_options.newline_above")); + dropdown.addOption("newLineBelow", t("builder.capture.pos_options.newline_below")); } - dropdown.addOption("after", "After line…"); - dropdown.addOption("bottom", "Bottom of file"); + dropdown.addOption("after", t("builder.capture.pos_options.after")); + dropdown.addOption("bottom", t("builder.capture.pos_options.bottom")); dropdown.setValue(current); dropdown.onChange((value: string) => { const v = value as @@ -438,14 +430,9 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { } private addInsertAfterFields() { - const descText = - "Insert capture after specified text. Accepts format syntax. " + - "Tip: use a heading (starts with #) to target a section. " + - "Blank line handling is configurable below."; - new Setting(this.contentEl) - .setName("Insert after") - .setDesc(descText); + .setName(t("builder.capture.insert_after")) + .setDesc(t("builder.capture.insert_after_desc")); const displayFormatter: FormatDisplayFormatter = new FormatDisplayFormatter( this.app, @@ -453,18 +440,18 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { ); const previewRow = this.contentEl.createDiv({ cls: "qa-preview-row" }); - previewRow.createEl("span", { text: "Preview: ", cls: "qa-preview-label" }); + previewRow.createEl("span", { text: t("builder.common.preview"), cls: "qa-preview-label" }); const previewValue = previewRow.createEl("span"); previewValue.setAttribute("aria-live", "polite"); - previewValue.innerText = "Loading preview…"; + previewValue.innerText = t("builder.common.loading"); createValidatedInput({ app: this.app, parent: this.contentEl, initialValue: this.choice.insertAfter.after, - placeholder: "Insert after", + placeholder: t("builder.capture.insert_after"), required: true, - requiredMessage: "Insert after text is required", + requiredMessage: t("builder.capture.insert_after_required"), attachSuggesters: [ (el) => new FormatSyntaxSuggester(this.app, el, this.plugin), ], @@ -473,7 +460,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { try { previewValue.innerText = await displayFormatter.format(value); } catch { - previewValue.innerText = "Preview unavailable"; + previewValue.innerText = t("builder.common.unavailable"); } }, }); @@ -484,7 +471,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.choice.insertAfter.after, ); } catch { - previewValue.innerText = "Preview unavailable"; + previewValue.innerText = t("builder.common.unavailable"); } })(); @@ -497,10 +484,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { } new Setting(this.contentEl) - .setName("Inline insertion") - .setDesc( - "Insert captured content on the same line, immediately after the matched text (no newline added).", - ) + .setName(t("builder.capture.inline")) + .setDesc(t("builder.capture.inline_desc")) .addToggle((toggle) => toggle .setValue(!!this.choice.insertAfter?.inline) @@ -512,8 +497,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { if (this.choice.insertAfter.inline) { new Setting(this.contentEl) - .setName("Replace existing value") - .setDesc("Replace everything after the matched text up to end-of-line.") + .setName(t("builder.capture.replace_existing")) + .setDesc(t("builder.capture.replace_existing_desc")) .addToggle((toggle) => toggle .setValue(!!this.choice.insertAfter?.replaceExisting) @@ -528,10 +513,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { if (!inlineEnabled) { const insertAtEndSetting: Setting = new Setting(this.contentEl); insertAtEndSetting - .setName("Insert at end of section") - .setDesc( - "Place the text at the end of the matched section instead of the top.", - ) + .setName(t("builder.capture.insert_end_section")) + .setDesc(t("builder.capture.insert_end_section_desc")) .addToggle((toggle) => toggle .setValue(this.choice.insertAfter?.insertAtEnd) @@ -545,22 +528,20 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.choice.insertAfter.blankLineAfterMatchMode = "auto"; } - const blankLineModeDesc = - "Controls whether Insert After skips existing blank lines after the matched line."; const insertAtEndEnabled = !!this.choice.insertAfter?.insertAtEnd; const blankLineModeSetting: Setting = new Setting(this.contentEl); blankLineModeSetting - .setName("Blank lines after match") + .setName(t("builder.capture.blank_lines_after_match")) .setDesc( insertAtEndEnabled - ? "Not used when inserting at end of section." - : blankLineModeDesc, + ? t("builder.capture.blank_lines_not_used") + : t("builder.capture.blank_lines_desc"), ) .addDropdown((dropdown) => { dropdown - .addOption("auto", "Auto (headings only)") - .addOption("skip", "Always skip") - .addOption("none", "Never skip") + .addOption("auto", t("builder.capture.blank_lines_options.auto")) + .addOption("skip", t("builder.capture.blank_lines_options.skip")) + .addOption("none", t("builder.capture.blank_lines_options.none")) .setValue( this.choice.insertAfter?.blankLineAfterMatchMode ?? "auto", ) @@ -575,10 +556,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { blankLineModeSetting.setDisabled(insertAtEndEnabled); new Setting(this.contentEl) - .setName("Consider subsections") - .setDesc( - "Also include the section’s subsections (requires target to be a heading starting with #). Subsections are headings inside the section.", - ) + .setName(t("builder.capture.consider_subsections")) + .setDesc(t("builder.capture.consider_subsections_desc")) .addToggle((toggle) => toggle .setValue(this.choice.insertAfter?.considerSubsections) @@ -596,9 +575,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.choice.insertAfter.considerSubsections = false; // reset the toggle to match state and inform user toggle.setValue(false); - new Notice( - "Consider subsections requires the target to be a heading (starts with #)", - ); + new Notice(t("builder.capture.consider_subsections_notice")); } }), ); @@ -606,8 +583,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { const createLineIfNotFound: Setting = new Setting(this.contentEl); createLineIfNotFound - .setName("Create line if not found") - .setDesc("Creates the 'insert after' line if it is not found.") + .setName(t("builder.capture.create_line_if_not_found")) + .setDesc(t("builder.capture.create_line_if_not_found_desc")) .addToggle((toggle) => { if (!this.choice.insertAfter?.createIfNotFound) this.choice.insertAfter.createIfNotFound = false; // Set to default @@ -624,9 +601,9 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { CREATE_IF_NOT_FOUND_TOP; // Set to default dropdown - .addOption(CREATE_IF_NOT_FOUND_TOP, "Top") - .addOption(CREATE_IF_NOT_FOUND_BOTTOM, "Bottom") - .addOption(CREATE_IF_NOT_FOUND_CURSOR, "Cursor") + .addOption(CREATE_IF_NOT_FOUND_TOP, t("builder.capture.position_options.top")) + .addOption(CREATE_IF_NOT_FOUND_BOTTOM, t("builder.capture.position_options.bottom")) + .addOption(CREATE_IF_NOT_FOUND_CURSOR, t("builder.capture.position_options.cursor")) .setValue(this.choice.insertAfter?.createIfNotFoundLocation) .onChange( (value) => @@ -638,8 +615,8 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { private addFormatSetting() { const enableSetting = new Setting(this.contentEl); enableSetting - .setName("Capture format") - .setDesc("Set the format of the capture.") + .setName(t("builder.capture.format")) + .setDesc(t("builder.capture.format_desc")) .addToggle((toggleComponent) => { toggleComponent .setValue(this.choice.format.enabled) @@ -656,19 +633,19 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { ); const previewRow = this.contentEl.createDiv({ cls: "qa-preview-row" }); - previewRow.createEl("span", { text: "Preview: ", cls: "qa-preview-label" }); + previewRow.createEl("span", { text: t("builder.common.preview"), cls: "qa-preview-label" }); const formatDisplay = previewRow.createEl("span"); formatDisplay.setAttr("aria-live", "polite"); - formatDisplay.innerText = "Loading preview…"; + formatDisplay.innerText = t("builder.common.loading"); const formatHandle = createValidatedInput({ app: this.app, parent: this.contentEl, inputKind: "textarea", initialValue: this.choice.format.format, - placeholder: "Format", + placeholder: t("builder.capture.format"), required: this.choice.format.enabled, - requiredMessage: "Capture format is required when enabled", + requiredMessage: t("builder.capture.format_required"), attachSuggesters: [ (el) => new FormatSyntaxSuggester(this.app, el, this.plugin), ], @@ -677,7 +654,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { try { formatDisplay.innerText = await displayFormatter.format(value); } catch { - formatDisplay.innerText = "Preview unavailable"; + formatDisplay.innerText = t("builder.common.unavailable"); } }, }); @@ -690,7 +667,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { this.choice.format.format, ); } catch { - formatDisplay.innerText = "Preview unavailable"; + formatDisplay.innerText = t("builder.common.unavailable"); } })(); } @@ -705,11 +682,11 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { const createFileIfItDoesntExist: Setting = new Setting(this.contentEl); createFileIfItDoesntExist - .setName("Create file if it doesn't exist") + .setName(t("builder.capture.create_if_missing")) .addToggle((toggle) => toggle .setValue(this.choice?.createFileIfItDoesntExist?.enabled) - .setTooltip("Create file if it doesn't exist") + .setTooltip(t("builder.capture.create_if_missing_tooltip")) .onChange((value) => { this.choice.createFileIfItDoesntExist.enabled = value; this.reload(); @@ -721,7 +698,7 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { let templateSelector: TextComponent; const createWithTemplateSetting = new Setting(this.contentEl); createWithTemplateSetting - .setName("Create file with given template.") + .setName(t("builder.capture.create_with_template")) .addToggle((toggle) => toggle .setValue(this.choice.createFileIfItDoesntExist?.createWithTemplate) @@ -739,13 +716,13 @@ export class CaptureChoiceBuilder extends ChoiceBuilder { app: this.app, parent: this.contentEl, initialValue: this.choice?.createFileIfItDoesntExist?.template ?? "", - placeholder: "Template path", + placeholder: t("builder.common.template_path"), suggestions: templateFilePaths, maxSuggestions: 50, validator: (raw) => { const v = raw.trim(); if (!v) return true; - return templateFilePaths.includes(v) || "Template not found"; + return templateFilePaths.includes(v) || t("builder.common.template_not_found"); }, onChange: (value) => { this.choice.createFileIfItDoesntExist.template = value; diff --git a/src/gui/ChoiceBuilder/choiceBuilder.ts b/src/gui/ChoiceBuilder/choiceBuilder.ts index 3d31a30e..4d49d988 100644 --- a/src/gui/ChoiceBuilder/choiceBuilder.ts +++ b/src/gui/ChoiceBuilder/choiceBuilder.ts @@ -5,6 +5,7 @@ import type IChoice from "../../types/choices/IChoice"; import type { FileViewMode2, OpenLocation } from "../../types/fileOpening"; import GenericInputPrompt from "../GenericInputPrompt/GenericInputPrompt"; import { GenericTextSuggester } from "../suggesters/genericTextSuggester"; +import { t } from "../../i18n/i18n"; export abstract class ChoiceBuilder extends Modal { private resolvePromise: (input: IChoice) => void; @@ -36,15 +37,13 @@ export abstract class ChoiceBuilder extends Modal { protected addOnePageOverrideSetting(choice: IChoice): void { new Setting(this.contentEl) - .setName("One-page input override") - .setDesc( - "Override the global setting for this choice. 'Always' forces the one-page modal even if disabled globally; 'Never' disables it even if enabled globally.", - ) + .setName(t("builder.one_page_override.name")) + .setDesc(t("builder.one_page_override.desc")) .addDropdown((dropdown) => { dropdown.addOptions({ - "": "Follow global setting", - always: "Always", - never: "Never", + "": t("builder.one_page_override.follow"), + always: t("builder.one_page_override.always"), + never: t("builder.one_page_override.never"), }); dropdown.setValue((choice.onePageInput ?? "") as string); dropdown.onChange((val: string) => { @@ -60,7 +59,7 @@ export abstract class ChoiceBuilder extends Modal { ): void { setting.addSearch((searchComponent) => { searchComponent.setValue(value); - searchComponent.setPlaceholder("File path"); + searchComponent.setPlaceholder(t("builder.file_path_placeholder")); const markdownFiles: string[] = this.app.vault .getMarkdownFiles() @@ -88,7 +87,7 @@ export abstract class ChoiceBuilder extends Modal { const newName: string = await GenericInputPrompt.Prompt( this.app, choice.name, - "Choice name", + t("builder.choice_name"), choice.name, ); if (newName !== choice.name) { @@ -111,7 +110,7 @@ export abstract class ChoiceBuilder extends Modal { if (choice.openFile === undefined) return; // Guard: nothing to configure new Setting(this.contentEl) - .setName("Open") + .setName(t("builder.open")) .setDesc(description) .addToggle((toggle) => { toggle.setValue(choice.openFile); @@ -149,16 +148,16 @@ export abstract class ChoiceBuilder extends Modal { // Location selector new Setting(this.contentEl) - .setName("File Opening Location") - .setDesc(`Where to open the ${contextLabel} file`) + .setName(t("builder.location")) + .setDesc(t("builder.descriptions.location")) .addDropdown((dropdown) => { dropdown.addOptions({ - reuse: "Reuse current tab", - tab: "New tab", - split: "Split pane", - window: "New window", - "left-sidebar": "Left sidebar", - "right-sidebar": "Right sidebar", + reuse: t("builder.options.reuse"), + tab: t("builder.options.tab"), + split: t("builder.options.split"), + window: t("builder.options.window"), + "left-sidebar": t("builder.options.left"), + "right-sidebar": t("builder.options.right"), }); dropdown.setValue(fileOpening.location); dropdown.onChange((value: any) => { @@ -170,12 +169,12 @@ export abstract class ChoiceBuilder extends Modal { // Split direction – only if location === "split" if (fileOpening.location === "split") { new Setting(this.contentEl) - .setName("Split Direction") - .setDesc("Direction for split panes") + .setName(t("builder.split_direction")) + .setDesc(t("builder.descriptions.split")) .addDropdown((dropdown) => { dropdown.addOptions({ - vertical: "Vertical", - horizontal: "Horizontal", + vertical: t("builder.options.vertical"), + horizontal: t("builder.options.horizontal"), }); dropdown.setValue(fileOpening.direction); dropdown.onChange((value: any) => { @@ -186,14 +185,14 @@ export abstract class ChoiceBuilder extends Modal { // View mode selector new Setting(this.contentEl) - .setName("View Mode") - .setDesc("How to display the opened file") + .setName(t("builder.viewMode")) + .setDesc(t("builder.descriptions.view_mode")) .addDropdown((dropdown) => { dropdown.addOptions({ - source: "Source", - preview: "Preview", - live: "Live Preview", - default: "Default", + source: t("builder.options.source"), + preview: t("builder.options.preview"), + live: t("builder.options.live"), + default: t("builder.options.default"), }); dropdown.setValue( typeof fileOpening.mode === "string" @@ -208,8 +207,8 @@ export abstract class ChoiceBuilder extends Modal { // Focus toggle – only show for non-reuse locations if (fileOpening.location !== "reuse") { new Setting(this.contentEl) - .setName("Focus new pane") - .setDesc("Focus the opened tab immediately after opening") + .setName(t("builder.focus_pane")) + .setDesc(t("builder.descriptions.focus")) .addToggle((toggle) => toggle.setValue(fileOpening.focus).onChange((value) => { fileOpening.focus = value; diff --git a/src/gui/ChoiceBuilder/templateChoiceBuilder.ts b/src/gui/ChoiceBuilder/templateChoiceBuilder.ts index 437a06fe..a0f5bc85 100644 --- a/src/gui/ChoiceBuilder/templateChoiceBuilder.ts +++ b/src/gui/ChoiceBuilder/templateChoiceBuilder.ts @@ -28,6 +28,7 @@ import { ExclusiveSuggester } from "../suggesters/exclusiveSuggester"; import { FormatSyntaxSuggester } from "../suggesters/formatSyntaxSuggester"; import { ChoiceBuilder } from "./choiceBuilder"; import FolderList from "./FolderList.svelte"; +import { t } from "../../i18n/i18n"; export class TemplateChoiceBuilder extends ChoiceBuilder { choice: ITemplateChoice; @@ -48,22 +49,22 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { this.addCenteredChoiceNameHeader(this.choice); // Template - new Setting(this.contentEl).setName("Template").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.template")).setHeading(); this.addTemplatePathSetting(); this.addFileNameFormatSetting(); // Location - new Setting(this.contentEl).setName("Location").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.location")).setHeading(); this.addFolderSetting(); // Linking - new Setting(this.contentEl).setName("Linking").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.linking")).setHeading(); this.addAppendLinkSetting(); // Behavior - new Setting(this.contentEl).setName("Behavior").setHeading(); + new Setting(this.contentEl).setName(t("builder.common.behavior")).setHeading(); this.addFileAlreadyExistsSetting(); - this.addOpenFileSetting("Open the created file."); + this.addOpenFileSetting(t("builder.descriptions.location")); if (this.choice.openFile) { this.addFileOpeningSetting("created"); } @@ -72,8 +73,8 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { private addTemplatePathSetting(): void { new Setting(this.contentEl) - .setName("Template Path") - .setDesc("Path to the Template."); + .setName(t("builder.template.path")) + .setDesc(t("builder.template.path_desc")); const templates: string[] = this.plugin .getTemplateFiles() @@ -83,7 +84,7 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { app: this.app, parent: this.contentEl, initialValue: this.choice.templatePath, - placeholder: "Template path", + placeholder: t("builder.template.path"), suggestions: templates, maxSuggestions: 50, validator: (raw) => { @@ -101,7 +102,7 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { let textField: TextComponent; const enableSetting = new Setting(this.contentEl); enableSetting - .setName("File name format") + .setName(t("builder.template.file_name_format")) .setDesc("Set the file name format.") .addToggle((toggleComponent) => { toggleComponent @@ -114,24 +115,24 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { // Desc + preview row const previewRow = this.contentEl.createDiv({ cls: "qa-preview-row" }); - previewRow.createEl("span", { text: "Preview: ", cls: "qa-preview-label" }); + previewRow.createEl("span", { text: t("builder.common.preview"), cls: "qa-preview-label" }); const formatDisplay = previewRow.createEl("span"); formatDisplay.setAttr("aria-live", "polite"); const displayFormatter: FileNameDisplayFormatter = new FileNameDisplayFormatter(this.app, this.plugin); - formatDisplay.textContent = "Loading preview…"; + formatDisplay.textContent = t("builder.common.loading"); void (async () => { try { formatDisplay.textContent = await displayFormatter.format( this.choice.fileNameFormat.format, ); } catch { - formatDisplay.textContent = "Preview unavailable"; + formatDisplay.textContent = t("builder.common.unavailable"); } })(); const formatInput = new TextComponent(this.contentEl); - formatInput.setPlaceholder("File name format"); + formatInput.setPlaceholder(t("builder.template.file_name_format")); textField = formatInput; formatInput.inputEl.style.width = "100%"; formatInput.inputEl.style.marginBottom = "8px"; @@ -144,7 +145,7 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { try { formatDisplay.textContent = await displayFormatter.format(value); } catch { - formatDisplay.textContent = "Preview unavailable"; + formatDisplay.textContent = t("builder.common.unavailable"); } }); @@ -154,10 +155,8 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { private addFolderSetting(): void { const folderSetting: Setting = new Setting(this.contentEl); folderSetting - .setName("Create in folder") - .setDesc( - "Create the file in the specified folder. If multiple folders are specified, you will be prompted for which folder to create the file in.", - ) + .setName(t("builder.template.folder")) + .setDesc(t("builder.template.folder_desc")) .addToggle((toggle) => { toggle.setValue(this.choice.folder.enabled); toggle.onChange((value) => { @@ -175,7 +174,7 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { "chooseFolderWhenCreatingNoteContainer", ); chooseFolderWhenCreatingNoteContainer.createEl("span", { - text: "Choose folder when creating a new note", + text: t("builder.template.choose_folder"), }); const chooseFolderWhenCreatingNote: ToggleComponent = new ToggleComponent( chooseFolderWhenCreatingNoteContainer, @@ -196,7 +195,7 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { const stn = new Setting(chooseFolderFromSubfolderContainer); stn - .setName("Include subfolders") + .setName(t("builder.template.include_subfolders")) .setDesc( "Get prompted to choose from both the selected folders and their subfolders when creating the note.", ) @@ -215,7 +214,7 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { this.contentEl, ); createInSameFolderAsActiveFileSetting - .setName("Create in same folder as active file") + .setName(t("builder.template.same_folder")) .setDesc( "Creates the file in the same folder as the currently active file. Will not create the file if there is no active file.", ) @@ -316,12 +315,12 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { const appendLinkSetting: Setting = new Setting(this.contentEl); appendLinkSetting - .setName("Link to created file") - .setDesc("Choose how QuickAdd should insert a link to the created file in the current note.") + .setName(t("builder.append_link.name")) + .setDesc(t("builder.append_link.desc")) .addDropdown((dropdown) => { - dropdown.addOption("required", "Enabled (requires active file)"); - dropdown.addOption("optional", "Enabled (skip if no active file)"); - dropdown.addOption("disabled", "Disabled"); + dropdown.addOption("required", t("builder.append_link.options.required")); + dropdown.addOption("optional", t("builder.append_link.options.optional")); + dropdown.addOption("disabled", t("builder.append_link.options.disabled")); dropdown.setValue(currentMode); dropdown.onChange((value: AppendLinkMode) => { @@ -354,13 +353,13 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { if (currentMode !== "disabled") { const placementSetting: Setting = new Setting(this.contentEl); placementSetting - .setName("Link placement") + .setName(t("builder.append_link.placement")) .setDesc("Where to place the link when appending") .addDropdown((dropdown) => { - dropdown.addOption("replaceSelection", "Replace selection"); - dropdown.addOption("afterSelection", "After selection"); - dropdown.addOption("endOfLine", "End of line"); - dropdown.addOption("newLine", "New line"); + dropdown.addOption("replaceSelection", t("builder.append_link.options.replace")); + dropdown.addOption("afterSelection", t("builder.append_link.options.after")); + dropdown.addOption("endOfLine", t("builder.append_link.options.eol")); + dropdown.addOption("newLine", t("builder.append_link.options.newline")); dropdown.setValue(normalizedOptions.placement); dropdown.onChange((value: LinkPlacement) => { @@ -390,11 +389,11 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { if (placementSupportsEmbed(normalizedOptions.placement)) { const linkTypeSetting: Setting = new Setting(this.contentEl); linkTypeSetting - .setName("Link type") + .setName(t("builder.append_link.type")) .setDesc("Choose whether replacing the selection should insert a link or an embed.") .addDropdown((dropdown) => { - dropdown.addOption("link", "Link"); - dropdown.addOption("embed", "Embed"); + dropdown.addOption("link", t("builder.append_link.options.link")); + dropdown.addOption("embed", t("builder.append_link.options.embed")); dropdown.setValue(normalizedLinkType); dropdown.onChange((value: LinkType) => { const currentValue = this.choice.appendLink; @@ -422,10 +421,8 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { private addFileAlreadyExistsSetting(): void { const fileAlreadyExistsSetting: Setting = new Setting(this.contentEl); fileAlreadyExistsSetting - .setName("Set default behavior if file already exists") - .setDesc( - "Set default behavior rather then prompting user on what to do if a file already exists.", - ) + .setName(t("builder.template.conflict")) + .setDesc(t("builder.template.conflict_desc")) .addToggle((toggle) => { toggle.setValue(this.choice.setFileExistsBehavior); toggle.onChange((value) => { @@ -439,11 +436,11 @@ export class TemplateChoiceBuilder extends ChoiceBuilder { this.choice.fileExistsMode = fileExistsDoNothing; dropdown - .addOption(fileExistsAppendToBottom, fileExistsAppendToBottom) - .addOption(fileExistsAppendToTop, fileExistsAppendToTop) - .addOption(fileExistsIncrement, fileExistsIncrement) - .addOption(fileExistsOverwriteFile, fileExistsOverwriteFile) - .addOption(fileExistsDoNothing, fileExistsDoNothing) + .addOption(fileExistsAppendToBottom, t("builder.template.conflict_options.append_bottom")) + .addOption(fileExistsAppendToTop, t("builder.template.conflict_options.append_top")) + .addOption(fileExistsIncrement, t("builder.template.conflict_options.increment")) + .addOption(fileExistsOverwriteFile, t("builder.template.conflict_options.overwrite")) + .addOption(fileExistsDoNothing, t("builder.template.conflict_options.nothing")) .setValue(this.choice.fileExistsMode) .onChange( (value: (typeof fileExistsChoices)[number]) => diff --git a/src/gui/MacroGUIs/MacroBuilder.ts b/src/gui/MacroGUIs/MacroBuilder.ts index b7f45b1a..ffa1cfc6 100644 --- a/src/gui/MacroGUIs/MacroBuilder.ts +++ b/src/gui/MacroGUIs/MacroBuilder.ts @@ -13,6 +13,7 @@ import { import type { IConditionalCommand } from "../../types/macros/Conditional/IConditionalCommand"; import { ConditionalCommandSettingsModal } from "./ConditionalCommandSettingsModal"; import { ConditionalBranchEditorModal } from "./ConditionalBranchEditorModal"; +import { t } from "../../i18n/i18n"; function getChoicesAsList(nestedChoices: IChoice[]): IChoice[] { const arr: IChoice[] = []; @@ -83,7 +84,7 @@ export class MacroBuilder extends Modal { headerEl.addEventListener("click", async () => { const newName: string = await GenericInputPrompt.Prompt( this.app, - `Update name for ${this.choice.name}`, + `${t("macro.renaming")} ${this.choice.name}`, this.choice.name, this.choice.name ); @@ -98,8 +99,8 @@ export class MacroBuilder extends Modal { private addRunOnStartupSetting(): void { new Setting(this.contentEl) - .setName("Run on startup") - .setDesc("Execute this macro when Obsidian starts") + .setName(t("macro.run_on_startup")) + .setDesc(t("macro.run_on_startup_desc")) .addToggle(toggle => toggle .setValue(this.choice.runOnStartup) .onChange(value => { @@ -153,12 +154,12 @@ export class MacroBuilder extends Modal { command: IConditionalCommand, branch: "then" | "else" ): Promise { - const title = branch === "then" ? "Then branch" : "Else branch"; + const title = branch === "then" ? t("macro.branches.then") : t("macro.branches.else"); const modal = new ConditionalBranchEditorModal({ app: this.app, plugin: this.plugin, choices: this.choices, - title: `Edit ${title} commands`, + title: `${t("macro.branches.edit")} ${title}`, commands: branch === "then" ? command.thenCommands : command.elseCommands, conditionalHandlers: this.buildConditionalHandlers(), }); diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts new file mode 100644 index 00000000..1356ecfd --- /dev/null +++ b/src/i18n/i18n.ts @@ -0,0 +1,15 @@ +import { moment } from "obsidian"; +import en from "./locales/en.json"; +import fr from "./locales/fr.json"; + +const locales: { [key: string]: any } = { en, fr }; + +export function t(path: string): string { + const lang = moment.locale(); + const locale = locales[lang] || locales.en; + + // Permet d'accéder aux objets imbriqués (ex: "settings.headers.choices") + const value = path.split('.').reduce((obj, key) => obj?.[key], locale); + + return value || path; +} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json new file mode 100644 index 00000000..e080479a --- /dev/null +++ b/src/i18n/locales/en.json @@ -0,0 +1,267 @@ +{ + "commands": { + "run": "Run QuickAdd", + "reload": "Reload QuickAdd (dev)", + "test": "Test QuickAdd (dev)" + }, + "ribbon": { + "icon": "QuickAdd" + }, + "settings": { + "headers": { + "choices": "Choices & Packages", + "input": "Input", + "templates": "Templates & Properties", + "notifications": "Notifications", + "globals": "Global Variables", + "ai_online": "AI & Online", + "appearance": "Appearance", + "dev": "Developer" + }, + "packages": { + "name": "Packages", + "desc": "Bundle or import QuickAdd automations as reusable packages.", + "export": "Export package...", + "import": "Import package..." + }, + "announce": { + "name": "Announce Updates", + "desc": "Display release notes when a new version is installed." + }, + "capture_notif": { + "name": "Show Capture Notifications", + "desc": "Display a notification when content is captured successfully." + }, + "cancel_notif": { + "name": "Show Input Cancellation Notifications", + "desc": "Display a notification when an input prompt is cancelled." + }, + "multiline": { + "name": "Use Multi-line Input Prompt", + "desc": "Use multi-line input prompt instead of single-line input prompt." + }, + "persist": { + "name": "Persist Input Prompt Drafts", + "desc": "Keep drafts when closing input prompts so they can be restored on reopen." + }, + "selection_capture": { + "name": "Use editor selection as default Capture value", + "desc": "When enabled, Capture uses the current editor selection as {{VALUE}}." + }, + "template_folder": { + "name": "Template Folder Path", + "desc": "Path to the folder where templates are stored." + }, + "template_properties": { + "name": "Format template variables as proper property types (Beta)", + "desc": "When enabled, template variables in front matter will be formatted as proper Obsidian property types." + }, + "one_page": { + "name": "One-page input for choices (Beta)", + "desc": "Resolve variables up front and show a single dynamic form." + }, + "date_aliases": { + "name": "Date aliases", + "desc": "Shortcodes for natural language date parsing (e.g. tm = tomorrow).", + "reset": "Reset" + }, + "disable_online": { + "name": "Disable AI & Online features", + "desc": "Prevents the plugin from making requests to external providers like OpenAI." + }, + "ribbon_options": { + "name": "Show icon in sidebar", + "desc": "Add QuickAdd icon to the sidebar ribbon." + }, + "dev": { + "info_name": "Development Information", + "info_desc": "Git information.", + "branch": "Branch", + "commit": "Commit", + "changes": "Uncommitted changes", + "yes": "Yes", + "no": "No" + } + }, + "builder": { + "open": "Open", + "viewMode": "View Mode", + "location": "File Opening Location", + "one_page_override": { + "name": "One-page input override", + "desc": "Override the global setting for this choice.", + "follow": "Follow global setting", + "always": "Always", + "never": "Never" + }, + "file_path_placeholder": "File path", + "choice_name": "Choice name", + "split_direction": "Split Direction", + "focus_pane": "Focus new pane", + "descriptions": { + "location": "Where to open the file", + "split": "Direction for split panes", + "view_mode": "How to display the opened file", + "focus": "Focus the opened tab immediately after opening" + }, + "options": { + "reuse": "Reuse current tab", + "tab": "New tab", + "split": "Split pane", + "window": "New window", + "left": "Left sidebar", + "right": "Right sidebar", + "source": "Source", + "preview": "Preview", + "live": "Live Preview", + "default": "Default", + "vertical": "Vertical", + "horizontal": "Horizontal" + }, + "common": { + "template": "Template", + "template_path": "Template path", + "template_not_found": "Template not found", + "add": "Add", + "location": "Location", + "linking": "Linking", + "behavior": "Behavior", + "content": "Content", + "position": "Position", + "preview": "Preview: ", + "loading": "Loading...", + "unavailable": "Unavailable" + }, + "template": { + "path": "Template Path", + "path_desc": "Path to the Template.", + "file_name_format": "File name format", + "file_name_format_desc": "Set the file name format.", + "folder": "Create in folder", + "folder_desc": "Create the file in the specified folder.", + "folder_path": "Folder path", + "choose_folder": "Choose folder when creating a new note", + "include_subfolders": "Include subfolders", + "include_subfolders_desc": "Get prompted to choose from both the selected folders and their subfolders when creating the note.", + "same_folder": "Create in same folder as active file", + "same_folder_desc": "Creates the file in the same folder as the currently active file.", + "conflict": "Set default behavior if file already exists", + "conflict_desc": "Set default behavior rather then prompting user.", + "conflict_options": { + "nothing": "Do nothing", + "overwrite": "Overwrite", + "increment": "Increment file name", + "append_bottom": "Append to bottom", + "append_top": "Append to top" + } + }, + "capture": { + "to": "Capture to", + "to_desc": "Target file path. Supports format syntax.", + "active_file": "Capture to active file", + "file_path": "File path / format", + "file_path_desc": "Choose a file or use format syntax (e.g., {{DATE}})", + "file_name_format": "File name format", + "open_file_desc": "Open the captured file.", + "task": "Task", + "task_desc": "Formats the value as a task.", + "format": "Capture format", + "format_desc": "Set the format of the capture.", + "format_required": "Capture format is required when enabled", + "create_if_missing": "Create file if it doesn't exist", + "create_if_missing_tooltip": "Create file if it doesn't exist", + "create_with_template": "Create file with given template", + "templater_after_capture": "Run Templater on entire destination file after capture", + "templater_after_capture_desc": "Advanced / legacy: this executes any `<% %>` anywhere in the destination file.", + "write_pos": "Write position", + "write_pos_desc": "Where to place the capture in the file.", + "pos_options": { + "top": "Top of file", + "bottom": "Bottom of file", + "cursor": "At cursor", + "after": "After line...", + "active_top": "Top (after frontmatter)", + "newline_above": "New line above", + "newline_below": "New line below" + }, + "selection_options": { + "use": "Use selection", + "ignore": "Ignore selection" + }, + "insert_after": "Insert after", + "insert_after_desc": "Insert capture after specified text. Accepts format syntax.", + "insert_after_required": "Insert after text is required", + "inline": "Inline insertion", + "inline_desc": "Insert captured content on the same line.", + "replace_existing": "Replace existing value", + "replace_existing_desc": "Replace everything after the matched text up to end-of-line.", + "insert_end_section": "Insert at end of section", + "insert_end_section_desc": "Place the text at the end of the matched section instead of the top.", + "blank_lines_after_match": "Blank lines after match", + "blank_lines_desc": "Controls whether Insert After skips existing blank lines after the matched line.", + "blank_lines_not_used": "Not used when inserting at end of section.", + "blank_lines_options": { + "auto": "Auto (headings only)", + "skip": "Always skip", + "none": "Never skip" + }, + "consider_subsections": "Consider subsections", + "consider_subsections_desc": "Also include the section's subsections (requires target to be a heading starting with #).", + "consider_subsections_notice": "Consider subsections requires the target to be a heading (starts with #)", + "create_line_if_not_found": "Create line if not found", + "create_line_if_not_found_desc": "Creates the 'insert after' line if it is not found.", + "position_options": { + "top": "Top", + "bottom": "Bottom", + "cursor": "Cursor" + } + }, + "append_link": { + "name": "Link to file", + "desc": "Choose how QuickAdd should insert a link to the file in the current note.", + "placement": "Link placement", + "placement_desc": "Where to place the link when appending", + "type": "Link type", + "type_desc": "Choose whether replacing the selection should insert a link or an embed.", + "options": { + "required": "Enabled (requires active file)", + "optional": "Enabled (skip if no active file)", + "disabled": "Disabled", + "replace": "Replace selection", + "after": "After selection", + "eol": "End of line", + "newline": "New line", + "link": "Link", + "embed": "Embed" + } + } + }, + "macro": { + "run_on_startup": "Run on startup", + "run_on_startup_desc": "Execute this macro when Obsidian starts.", + "renaming": "Update name for", + "branches": { + "then": "Then branch", + "else": "Else branch", + "edit": "Edit" + }, + "abort": "Macro execution aborted", + "input_cancelled": "Input cancelled by user", + "errors": { + "no_commands": "No commands in the macro for choice: ", + "load_script_failed": "Failed to load user script: ", + "run_script_failed": "Failed to run user script: ", + "script_invalid": "User script is invalid for choice: ", + "script_empty": "User script is an empty object for choice: ", + "choice_not_found": "Choice could not be found.", + "nested_choice_invalid": "Choice is invalid in: ", + "ai_disabled": "Blocking request to OpenAI: Online features are disabled in settings.", + "model_not_found": "Model not found: ", + "provider_not_found": "Provider not found for model: ", + "conditional_boolean": "Conditional script must return a boolean result: ", + "open_file_traversal": "OpenFile: Path traversal not allowed in ", + "open_file_missing": "OpenFile: Does not exist or is not a file: ", + "open_file_failed": "OpenFile: Failed to open file: " + } + } +} diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json new file mode 100644 index 00000000..18f2758f --- /dev/null +++ b/src/i18n/locales/fr.json @@ -0,0 +1,267 @@ +{ + "commands": { + "run": "Lancer QuickAdd", + "reload": "Recharger QuickAdd (dev)", + "test": "Tester QuickAdd (dev)" + }, + "ribbon": { + "icon": "QuickAdd" + }, + "settings": { + "headers": { + "choices": "Choix & Packages", + "input": "Saisie (Input)", + "templates": "Modèles & Propriétés", + "notifications": "Notifications", + "globals": "Variables Globales", + "ai_online": "IA & En ligne", + "appearance": "Apparence", + "dev": "Développeur" + }, + "packages": { + "name": "Packages", + "desc": "Regroupez ou importez des automatisations QuickAdd sous forme de packages réutilisables.", + "export": "Exporter le package…", + "import": "Importer le package…" + }, + "announce": { + "name": "Annoncer les mises à jour", + "desc": "Affiche les notes de version lors de l'installation d'une nouvelle version." + }, + "capture_notif": { + "name": "Notifications de capture", + "desc": "Affiche une notification lorsqu'un contenu est capturé avec succès." + }, + "cancel_notif": { + "name": "Notifications d'annulation", + "desc": "Affiche une notification lorsqu'une invite de saisie est annulée." + }, + "multiline": { + "name": "Saisie multiligne", + "desc": "Utilise une zone de texte multiligne pour les invites de saisie." + }, + "persist": { + "name": "Conserver les brouillons", + "desc": "Garde les brouillons lors de la fermeture des invites pour les restaurer." + }, + "selection_capture": { + "name": "Utiliser la sélection comme valeur par défaut", + "desc": "Si activé, la capture utilise le texte sélectionné pour {{VALUE}}." + }, + "template_folder": { + "name": "Dossier des modèles", + "desc": "Chemin du dossier où sont stockés les modèles." + }, + "template_properties": { + "name": "Formater les variables comme propriétés (Beta)", + "desc": "Formate les variables du frontmatter selon le type de propriété Obsidian." + }, + "one_page": { + "name": "Saisie sur une page (Beta)", + "desc": "Affiche un formulaire unique avant d'exécuter les choix." + }, + "date_aliases": { + "name": "Alias de date", + "desc": "Raccourcis pour l'analyse des dates (ex: tm = tomorrow).", + "reset": "Réinitialiser" + }, + "disable_online": { + "name": "Désactiver IA & En ligne", + "desc": "Empêche le plugin de faire des requêtes externes (comme OpenAI)." + }, + "ribbon_options": { + "name": "Afficher l'icône latérale", + "desc": "Ajoute l'icône QuickAdd au ruban latéral gauche." + }, + "dev": { + "info_name": "Infos dev", + "info_desc": "Infos Git.", + "branch": "Branche", + "commit": "Commit", + "changes": "Changements", + "yes": "Oui", + "no": "Non" + } + }, + "builder": { + "open": "Ouvrir", + "viewMode": "Mode de vue", + "location": "Emplacement d'ouverture", + "one_page_override": { + "name": "Forçage de la saisie sur une page", + "desc": "Remplace le paramètre global pour ce choix.", + "follow": "Suivre global", + "always": "Toujours", + "never": "Jamais" + }, + "file_path_placeholder": "Chemin du fichier", + "choice_name": "Nom du choix", + "split_direction": "Orientation de la séparation", + "focus_pane": "Focus sur le panneau", + "descriptions": { + "location": "Où ouvrir le fichier.", + "split": "Direction de la séparation.", + "view_mode": "Comment afficher le fichier.", + "focus": "Mettre le focus sur l'onglet." + }, + "options": { + "reuse": "Réutiliser l'onglet", + "tab": "Nouvel onglet", + "split": "Séparer le panneau", + "window": "Nouvelle fenêtre", + "left": "Barre gauche", + "right": "Barre droite", + "source": "Source", + "preview": "Lecture", + "live": "Live Preview", + "default": "Défaut", + "vertical": "Vertical", + "horizontal": "Horizontal" + }, + "common": { + "template": "Modèle", + "template_path": "Chemin du modèle", + "template_not_found": "Modèle introuvable", + "add": "Ajouter", + "location": "Emplacement", + "linking": "Liaison (Links)", + "behavior": "Comportement", + "content": "Contenu", + "position": "Position", + "preview": "Aperçu : ", + "loading": "Chargement...", + "unavailable": "Indisponible" + }, + "template": { + "path": "Chemin du modèle", + "path_desc": "Chemin vers le fichier modèle.", + "file_name_format": "Format du nom de fichier", + "file_name_format_desc": "Définir le format du nom de fichier.", + "folder": "Créer dans le dossier", + "folder_desc": "Créer le fichier dans ce dossier.", + "folder_path": "Chemin du dossier", + "choose_folder": "Choisir le dossier lors de la création", + "include_subfolders": "Inclure les sous-dossiers", + "include_subfolders_desc": "Proposer de choisir parmi les dossiers sélectionnés et leurs sous-dossiers.", + "same_folder": "Créer dans le même dossier que le fichier actif", + "same_folder_desc": "Crée le fichier dans le même dossier que le fichier actuellement actif.", + "conflict": "Comportement si le fichier existe déjà", + "conflict_desc": "Action par défaut au lieu de demander à l'utilisateur.", + "conflict_options": { + "nothing": "Ne rien faire", + "overwrite": "Écraser", + "increment": "Incrémenter le nom", + "append_bottom": "Ajouter à la fin", + "append_top": "Ajouter au début" + } + }, + "capture": { + "to": "Capturer vers", + "to_desc": "Chemin du fichier cible. Supporte la syntaxe de format.", + "active_file": "Capturer vers le fichier actif", + "file_path": "Chemin / Format", + "file_path_desc": "Choisir un fichier ou utiliser une syntaxe (ex: {{DATE}})", + "file_name_format": "Format du nom de fichier", + "open_file_desc": "Ouvrir le fichier capturé.", + "task": "Tâche", + "task_desc": "Formate la valeur comme une tâche à cocher.", + "format": "Format de capture", + "format_desc": "Définir le format du texte capturé.", + "format_required": "Le format de capture est requis s'il est activé", + "create_if_missing": "Créer le fichier s'il n'existe pas", + "create_if_missing_tooltip": "Créer le fichier s'il n'existe pas", + "create_with_template": "Créer avec un modèle spécifique", + "templater_after_capture": "Lancer Templater après la capture", + "templater_after_capture_desc": "Avancé : exécute les codes `<% %>` dans tout le fichier de destination.", + "write_pos": "Position d'écriture", + "write_pos_desc": "Où placer la capture dans le fichier.", + "pos_options": { + "top": "Haut du fichier", + "bottom": "Bas du fichier", + "cursor": "Au curseur", + "after": "Après la ligne...", + "active_top": "Haut (après frontmatter)", + "newline_above": "Nouvelle ligne au-dessus", + "newline_below": "Nouvelle ligne en-dessous" + }, + "selection_options": { + "use": "Utiliser la sélection", + "ignore": "Ignorer la sélection" + }, + "insert_after": "Insérer après", + "insert_after_desc": "Insérer après le texte spécifié. Accepte la syntaxe de format.", + "insert_after_required": "Le texte 'Insérer après' est requis", + "inline": "Insertion en ligne (Inline)", + "inline_desc": "Insère sur la même ligne, juste après le texte trouvé.", + "replace_existing": "Remplacer la valeur existante", + "replace_existing_desc": "Remplace tout après le texte trouvé jusqu'à la fin de la ligne.", + "insert_end_section": "Insérer à la fin de la section", + "insert_end_section_desc": "Place le texte à la fin de la section trouvée au lieu du début.", + "blank_lines_after_match": "Lignes vides après correspondance", + "blank_lines_desc": "Contrôle si l'insertion doit sauter les lignes vides existantes.", + "blank_lines_not_used": "Non utilisé lors de l'insertion en fin de section.", + "blank_lines_options": { + "auto": "Auto (titres uniquement)", + "skip": "Toujours sauter", + "none": "Ne jamais sauter" + }, + "consider_subsections": "Inclure les sous-sections", + "consider_subsections_desc": "Inclure aussi les sous-sections (nécessite que la cible soit un titre #).", + "consider_subsections_notice": "L'option nécessite que la cible soit un titre (commence par #)", + "create_line_if_not_found": "Créer la ligne si non trouvée", + "create_line_if_not_found_desc": "Crée la ligne 'Insérer après' si elle n'existe pas.", + "position_options": { + "top": "Haut", + "bottom": "Bas", + "cursor": "Curseur" + } + }, + "append_link": { + "name": "Lien vers le fichier créé/capturé", + "desc": "Insérer un lien vers ce fichier dans la note actuelle.", + "placement": "Placement du lien", + "placement_desc": "Où placer le lien lors de l'ajout.", + "type": "Type de lien", + "type_desc": "Choisir entre un lien simple ou une intégration (embed) lors du remplacement.", + "options": { + "required": "Activé (requiert un fichier actif)", + "optional": "Activé (ignorer si aucun fichier actif)", + "disabled": "Désactivé", + "replace": "Remplacer la sélection", + "after": "Après la sélection", + "eol": "Fin de ligne", + "newline": "Nouvelle ligne", + "link": "Lien", + "embed": "Intégration (Embed)" + } + } + }, + "macro": { + "run_on_startup": "Lancer au démarrage", + "run_on_startup_desc": "Exécute cette macro au démarrage d'Obsidian.", + "renaming": "Renommer", + "branches": { + "then": "Branche Alors (Then)", + "else": "Branche Sinon (Else)", + "edit": "Modifier :" + }, + "abort": "Exécution de la macro interrompue", + "input_cancelled": "Saisie annulée par l'utilisateur", + "errors": { + "no_commands": "Aucune commande dans la macro pour le choix : ", + "load_script_failed": "Échec du chargement du script utilisateur : ", + "run_script_failed": "Échec de l'exécution du script utilisateur : ", + "script_invalid": "Le script utilisateur est invalide pour le choix : ", + "script_empty": "Le script utilisateur est un objet vide pour le choix : ", + "choice_not_found": "Le choix n'a pas pu être trouvé.", + "nested_choice_invalid": "Le choix imbriqué est invalide dans : ", + "ai_disabled": "Requête OpenAI bloquée : Les fonctionnalités en ligne sont désactivées dans les paramètres.", + "model_not_found": "Modèle introuvable : ", + "provider_not_found": "Fournisseur introuvable pour le modèle : ", + "conditional_boolean": "Le script conditionnel doit renvoyer un booléen : ", + "open_file_traversal": "OpenFile : Traversée de chemin interdite dans ", + "open_file_missing": "OpenFile : Fichier inexistant ou invalide : ", + "open_file_failed": "OpenFile : Échec de l'ouverture du fichier : " + } + } +} diff --git a/src/main.ts b/src/main.ts index 8b46800a..3801a97d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,6 +23,7 @@ import { CommandType } from "./types/macros/CommandType"; import { InfiniteAIAssistantCommandSettingsModal } from "./gui/MacroGUIs/AIAssistantInfiniteCommandSettingsModal"; import { FieldSuggestionCache } from "./utils/FieldSuggestionCache"; import { isMajorUpdate } from "./utils/semver"; +import { t } from "./i18n/i18n"; // Parameters prefixed with `value-` get used as named values for the executed choice type CaptureValueParameters = { [key in `value-${string}`]?: string }; @@ -59,7 +60,7 @@ export default class QuickAdd extends Plugin { this.addCommand({ id: "runQuickAdd", - name: "Run QuickAdd", + name: t("commands.run"), callback: () => { ChoiceSuggester.Open(this, this.settings.choices); }, @@ -67,7 +68,7 @@ export default class QuickAdd extends Plugin { this.addCommand({ id: "reloadQuickAdd", - name: "Reload QuickAdd (dev)", + name: t("commands.reload"), checkCallback: (checking) => { if (checking) { return this.settings.devMode; @@ -87,7 +88,7 @@ export default class QuickAdd extends Plugin { this.addCommand({ id: "testQuickAdd", - name: "Test QuickAdd (dev)", + name: t("commands.test"), checkCallback: (checking) => { if (checking) { return this.settings.devMode; @@ -150,7 +151,7 @@ export default class QuickAdd extends Plugin { log.register(new ConsoleErrorLogger()).register(new GuiLogger(this)); if (this.settings.enableRibbonIcon) { - this.addRibbonIcon("file-plus", "QuickAdd", () => { + this.addRibbonIcon("file-plus", t("ribbon.icon"), () => { ChoiceSuggester.Open(this, this.settings.choices); }); } diff --git a/src/quickAddSettingsTab.ts b/src/quickAddSettingsTab.ts index 711fb1ba..5a44a4d6 100644 --- a/src/quickAddSettingsTab.ts +++ b/src/quickAddSettingsTab.ts @@ -22,6 +22,7 @@ import { formatDateAliasLines, parseDateAliasLines, } from "./utils/dateAliases"; +import { t } from "./i18n/i18n"; type SettingGroupLike = { addSetting(cb: (setting: Setting) => void): void; @@ -50,37 +51,37 @@ export class QuickAddSettingsTab extends PluginSettingTab { const { containerEl } = this; containerEl.empty(); - const choicesGroup = this.createSettingGroup("Choices & Packages"); + const choicesGroup = this.createSettingGroup(t("settings.headers.choices")); this.addChoicesSetting(choicesGroup); this.addPackagesSetting(choicesGroup); - const inputGroup = this.createSettingGroup("Input"); + const inputGroup = this.createSettingGroup(t("settings.headers.input")); this.addUseMultiLineInputPromptSetting(inputGroup); this.addPersistInputPromptDraftsSetting(inputGroup); this.addUseSelectionAsValueSetting(inputGroup); this.addOnePageInputSetting(inputGroup); this.addDateAliasesSetting(inputGroup); - const templatesGroup = this.createSettingGroup("Templates & Properties"); + const templatesGroup = this.createSettingGroup(t("settings.headers.templates")); this.addTemplateFolderPathSetting(templatesGroup); this.addTemplatePropertyTypesSetting(templatesGroup); - const notificationsGroup = this.createSettingGroup("Notifications"); + const notificationsGroup = this.createSettingGroup(t("settings.headers.notifications")); this.addAnnounceUpdatesSetting(notificationsGroup); this.addShowCaptureNotificationSetting(notificationsGroup); this.addShowInputCancellationNotificationSetting(notificationsGroup); - const globalsGroup = this.createSettingGroup("Global Variables"); + const globalsGroup = this.createSettingGroup(t("settings.headers.globals")); this.addGlobalVariablesSetting(globalsGroup); - const onlineGroup = this.createSettingGroup("AI & Online"); + const onlineGroup = this.createSettingGroup(t("settings.headers.ai_online")); this.addDisableOnlineFeaturesSetting(onlineGroup); - const appearanceGroup = this.createSettingGroup("Appearance"); + const appearanceGroup = this.createSettingGroup(t("settings.headers.appearance")); this.addEnableRibbonIconSetting(appearanceGroup); if (__IS_DEV_BUILD__) { - const devGroup = this.createSettingGroup("Developer"); + const devGroup = this.createSettingGroup(t("settings.headers.dev")); this.addDevelopmentInfoSetting(devGroup); } } @@ -128,8 +129,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addDevelopmentInfoSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName("Development Information"); - setting.setDesc("Git information for developers."); + setting.setName(t("settings.dev.info_name")); + setting.setDesc(t("settings.dev.info_desc")); const infoContainer = setting.settingEl.createDiv(); infoContainer.style.marginTop = "10px"; @@ -138,25 +139,32 @@ export class QuickAddSettingsTab extends PluginSettingTab { if (__DEV_GIT_BRANCH__ !== null) { const branchDiv = infoContainer.createDiv(); - branchDiv.innerHTML = `Branch: ${__DEV_GIT_BRANCH__}`; + const branchLabel = branchDiv.createEl("strong"); + branchLabel.textContent = `${t("settings.dev.branch")}:`; + branchDiv.appendText(` ${__DEV_GIT_BRANCH__}`); branchDiv.style.marginBottom = "5px"; } if (__DEV_GIT_COMMIT__ !== null) { const commitDiv = infoContainer.createDiv(); - commitDiv.innerHTML = `Commit: ${__DEV_GIT_COMMIT__}`; + const commitLabel = commitDiv.createEl("strong"); + commitLabel.textContent = `${t("settings.dev.commit")}:`; + commitDiv.appendText(` ${__DEV_GIT_COMMIT__}`); commitDiv.style.marginBottom = "5px"; } if (__DEV_GIT_DIRTY__ !== null) { const statusDiv = infoContainer.createDiv(); + const statusLabel = statusDiv.createEl("strong"); + statusLabel.textContent = `${t("settings.dev.changes")}:`; const statusText = __DEV_GIT_DIRTY__ - ? "Yes (uncommitted changes)" - : "No"; - const statusColor = __DEV_GIT_DIRTY__ + ? `${t("settings.dev.yes")} (${t("settings.dev.changes")})` + : t("settings.dev.no"); + const statusSpan = statusDiv.createEl("span"); + statusSpan.textContent = ` ${statusText}`; + statusSpan.style.color = __DEV_GIT_DIRTY__ ? "var(--text-warning)" : "var(--text-success)"; - statusDiv.innerHTML = `Uncommitted changes: ${statusText}`; } }); } @@ -216,14 +224,12 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addPackagesSetting(group: SettingGroupLike): void { group.addSetting((setting) => { - setting.setName("Packages"); - setting.setDesc( - "Bundle or import QuickAdd automations as reusable packages.", - ); + setting.setName(t("settings.packages.name")); + setting.setDesc(t("settings.packages.desc")); setting.addButton((button) => button - .setButtonText("Export package…") + .setButtonText(t("settings.packages.export")) .setCta() .onClick(() => { const choicesSnapshot = settingsStore.getState().choices; @@ -237,7 +243,7 @@ export class QuickAddSettingsTab extends PluginSettingTab { ); setting.addButton((button) => - button.setButtonText("Import package…").onClick(() => { + button.setButtonText(t("settings.packages.import")).onClick(() => { const modal = new ImportPackageModal(this.app); modal.open(); }), @@ -247,19 +253,14 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addAnnounceUpdatesSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName("Announce Updates"); - setting.setDesc( - "Display release notes when a new version is installed. This includes new features, demo videos, and bug fixes.", - ); + setting.setName(t("settings.announce.name")); + setting.setDesc(t("settings.announce.desc")); setting.addDropdown((dropdown) => { const currentValue = settingsStore.getState().announceUpdates; dropdown - .addOption("all", "Show updates on each new release") - .addOption( - "major", - "Show updates only on major releases (new features, breaking changes)", - ) - .addOption("none", "Don't show") + .addOption("all", t("settings.announce.all")) + .addOption("major", t("settings.announce.major")) + .addOption("none", t("settings.announce.none")) .setValue(currentValue) .onChange((value) => { settingsStore.setState({ @@ -272,10 +273,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addShowCaptureNotificationSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName("Show Capture Notifications"); - setting.setDesc( - "Display a notification when content is captured successfully to confirm the operation completed.", - ); + setting.setName(t("settings.capture_notif.name")); + setting.setDesc(t("settings.capture_notif.desc")); setting.addToggle((toggle) => { toggle.setValue(settingsStore.getState().showCaptureNotification); toggle.onChange((value) => { @@ -287,10 +286,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addShowInputCancellationNotificationSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName("Show Input Cancellation Notifications"); - setting.setDesc( - "Display a notification when an input prompt is cancelled without submitting. Disable this to avoid extra notices when dismissing prompts.", - ); + setting.setName(t("settings.cancel_notif.name")); + setting.setDesc(t("settings.cancel_notif.desc")); setting.addToggle((toggle) => { toggle.setValue( settingsStore.getState().showInputCancellationNotification, @@ -306,14 +303,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addTemplatePropertyTypesSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName( - "Format template variables as proper property types (Beta)", - ); - setting.setDesc( - "When enabled, template variables in front matter will be formatted as proper Obsidian property types. " + - "Arrays become List properties, numbers become Number properties, booleans become Checkbox properties, etc. " + - "This is a beta feature that may have edge cases.", - ); + setting.setName(t("settings.template_properties.name")); + setting.setDesc(t("settings.template_properties.desc")); setting.addToggle((toggle) => { toggle.setValue(settingsStore.getState().enableTemplatePropertyTypes); toggle.onChange((value) => { @@ -330,14 +321,12 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addUseMultiLineInputPromptSetting(group: SettingGroupLike) { group.addSetting((setting) => { setting - .setName("Use Multi-line Input Prompt") - .setDesc( - "Use multi-line input prompt instead of single-line input prompt", - ) + .setName(t("settings.multiline.name")) + .setDesc(t("settings.multiline.desc")) .addToggle((toggle) => toggle .setValue(this.plugin.settings.inputPrompt === "multi-line") - .setTooltip("Use multi-line input prompt") + .setTooltip(t("settings.multiline.name")) .onChange((value) => { if (value) { settingsStore.setState({ @@ -356,10 +345,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addPersistInputPromptDraftsSetting(group: SettingGroupLike) { group.addSetting((setting) => { setting - .setName("Persist Input Prompt Drafts") - .setDesc( - "Keep drafts when closing input prompts so they can be restored on reopen. Drafts are stored only for this session.", - ) + .setName(t("settings.persist.name")) + .setDesc(t("settings.persist.desc")) .addToggle((toggle) => toggle .setValue(settingsStore.getState().persistInputPromptDrafts) @@ -376,10 +363,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addUseSelectionAsValueSetting(group: SettingGroupLike) { group.addSetting((setting) => { setting - .setName("Use editor selection as default Capture value") - .setDesc( - "When enabled, Capture uses the current editor selection as {{VALUE}} and may skip the prompt. When disabled, Capture always prompts for {{VALUE}}.", - ) + .setName(t("settings.selection_capture.name")) + .setDesc(t("settings.selection_capture.desc")) .addToggle((toggle) => toggle .setValue(settingsStore.getState().useSelectionAsCaptureValue) @@ -392,10 +377,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addTemplateFolderPathSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName("Template Folder Path"); - setting.setDesc( - "Path to the folder where templates are stored. Used to suggest template files when configuring QuickAdd.", - ); + setting.setName(t("settings.template_folder.name")); + setting.setDesc(t("settings.template_folder.desc")); setting.addText((text) => { text @@ -423,10 +406,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addOnePageInputSetting(group: SettingGroupLike) { group.addSetting((setting) => { setting - .setName("One-page input for choices (Beta)") - .setDesc( - "Experimental. Resolve variables up front and show a single dynamic form before executing Template/Capture choices. See Advanced → One-page Inputs in docs.", - ) + .setName(t("settings.one_page.name")) + .setDesc(t("settings.one_page.desc")) .addToggle((toggle) => toggle .setValue(settingsStore.getState().onePageInputEnabled) @@ -439,11 +420,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addDateAliasesSetting(group: SettingGroupLike) { group.addSetting((setting) => { - setting.setName("Date aliases"); - setting.setDesc( - "Shortcodes for natural language date parsing. " + - "One per line: alias = phrase. Example: tm = tomorrow.", - ); + setting.setName(t("settings.date_aliases.name")); + setting.setDesc(t("settings.date_aliases.desc")); setting.settingEl.style.alignItems = "flex-start"; setting.controlEl.style.display = "flex"; setting.controlEl.style.flexWrap = "wrap"; @@ -475,7 +453,7 @@ export class QuickAddSettingsTab extends PluginSettingTab { setting.addButton((button) => { button - .setButtonText("Reset to defaults") + .setButtonText(t("settings.date_aliases.reset")) .onClick(() => { settingsStore.setState({ dateAliases: DEFAULT_DATE_ALIASES, @@ -495,10 +473,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addDisableOnlineFeaturesSetting(group: SettingGroupLike) { group.addSetting((setting) => { setting - .setName("Disable AI & Online features") - .setDesc( - "This prevents the plugin from making requests to external providers like OpenAI. You can still use User Scripts to execute arbitrary code, including contacting external providers. However, this setting disables plugin features like the AI Assistant from doing so. You need to disable this setting to use the AI Assistant.", - ) + .setName(t("settings.disable_online.name")) + .setDesc(t("settings.disable_online.desc")) .addToggle((toggle) => toggle .setValue(settingsStore.getState().disableOnlineFeatures) @@ -516,10 +492,8 @@ export class QuickAddSettingsTab extends PluginSettingTab { private addEnableRibbonIconSetting(group: SettingGroupLike) { group.addSetting((setting) => { setting - .setName("Show icon in sidebar") - .setDesc( - "Add QuickAdd icon to the sidebar ribbon. Requires a reload.", - ) + .setName(t("settings.ribbon_options.name")) + .setDesc(t("settings.ribbon_options.desc")) .addToggle((toggle) => { toggle .setValue(settingsStore.getState().enableRibbonIcon) diff --git a/tsconfig.json b/tsconfig.json index b5050303..487718d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,22 @@ { - "compilerOptions": { - "baseUrl": ".", - "inlineSourceMap": true, - "inlineSources": true, - "module": "ESNext", - "target": "ES2020", - "allowJs": true, - "noImplicitAny": true, - "moduleResolution": "node", - "importHelpers": true, - "isolatedModules": true, - "types": ["svelte", "node"], - "strictNullChecks": true, - "lib": ["DOM", "ES5", "ES6", "ES7", "ES2022"], - "experimentalDecorators": true, - "verbatimModuleSyntax": true - }, - "include": ["**/*.ts"] + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES2020", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "types": ["svelte", "node"], + "strictNullChecks": true, + "lib": ["DOM", "ES5", "ES6", "ES7", "ES2022"], + "experimentalDecorators": true, + "verbatimModuleSyntax": true, + "resolveJsonModule": true, + "esModuleInterop": true + }, + "include": ["**/*.ts"] }