Skip to content

Commit

Permalink
feature: add i18n framework for defining and using translations
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiebrynes7 committed Aug 8, 2024
1 parent 2ab07c6 commit c0a8e2d
Show file tree
Hide file tree
Showing 4 changed files with 404 additions and 0 deletions.
60 changes: 60 additions & 0 deletions plugin/src/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { en } from "@/i18n/langs/en";
import type { Translations } from "@/i18n/translation";
import { DeepPartial, type DeepReadonly } from "@/utils/types";

// Global language instance. Expect this to be set once at startup based off
// the language value in the root HTML element inside Obsidian.
let language = "en";
let _t: DeepReadonly<Translations> = en as DeepReadonly<Translations>;

export const setLanguage = (lang: string) => {
language = lang;
_t = DeepPartial.merge(en, registry[lang] ?? {}) as DeepReadonly<Translations>;
};

export const t = () => {
return _t;
};

type TranslationStatus = { kind: "complete" | "missing" | "partial" };

export const getTranslationStatus = (): TranslationStatus => {
if (language === "en") {
return { kind: "complete" };
}
if (!(language in registry)) {
return { kind: "missing" };
}

// Check if all keys are present recursively.
const translations = registry[language];
if (DeepPartial.isComplete(en, translations)) {
return { kind: "complete" };
}

return { kind: "partial" };
};

/*
* Register new translations here. They do not need to be complete to be
* used. If keys are missing, they will be filled in with the default (en).
*
* 1. Identify the langauge code. This can be found in the root HTML element
* inside Obsidian.
* 2. Create a new file in the langs/ directory with the language code as the
* file name. E.g. - `langs/fr.ts`.
* 3. Copy the template below and fill in the missing keys. Make sure to replace
* the $langCode with your language code.
import type { Translations } from "@/i18n/translation";
import type { DeepPartial } from "@/utils/types";
export const $langCode: DeepPartial<Translations> = {};
* 4. Register the language in the registry below. The key should be the lang code.
The value should be the exported object from step 3.
*/

// Register new languages here. The key should be the language key as seen
// in the root of the HTML document (e.g. "fr", "es", etc.).
const registry: Record<string, DeepPartial<Translations>> = {};
177 changes: 177 additions & 0 deletions plugin/src/i18n/langs/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import type { Translations } from "@/i18n/translation";

export const en: Translations = {
settings: {
general: {
header: "General",
links: {
label: "Links",
docsButtonLabel: "Docs",
feedbackButtonLabel: "Feedback",
donateButtonLabel: "Donate",
},
apiToken: {
label: "API token",
description: "The Todoist API token to use when fetching tasks",
buttonLabel: "Setup",
},
},
autoRefresh: {
header: "Auto-refresh",
toggle: {
label: "Enable auto-refresh",
description: "Whether queries should auto-refresh at a set interval",
},
interval: {
label: "Auto-refresh interval",
description: "The interval, in seconds, that queries will be auto-refreshed by default",
},
},
rendering: {
header: "Rendering",
taskFadeAnimation: {
label: "Enable task fade animation",
description: "Whether tasks should fade in and out when created or completed",
},
dateIcon: {
label: "Enable dates icon",
description: "Whether rendered dates should include an icon",
},
projectIcon: {
label: "Enable project & section icon",
description: "Whether rendered projects & sections should include an icon",
},
labelsIcon: {
label: "Enable label icon",
description: "Whether rendered labels should include an icon",
},
},
taskCreation: {
header: "Task creation",
wrapLinksInParens: {
label: "Add parenthesis to page links",
description:
"When enabled, wraps Obsidian page links in Todoist tasks created from the command",
},
},
advanced: {
header: "Advanced",
debugLogging: {
label: "Enable debug logging",
description: "Whether debug logging should be enabled",
},
},
deprecation: {
warningMessage: "This setting is deprecated and will be removed in a future release.",
},
},
createTaskModal: {
loadingMessage: "Loading Todoist data...",
successNotice: "Task created successfully",
errorNotice: "Failed to create task",
taskNamePlaceholder: "Task name",
descriptionPlaceholder: "Description",
appendedLinkToContentMessage: "A link to this page will be appended to the task name",
appendedLinkToDescriptionMessage:
"A link to this page will be appended to the task description",
cancelButtonLabel: "Cancel",
addTaskButtonLabel: "Add task",
failedToFindInboxNotice: "Error: could not find inbox project",
dateSelector: {
buttonLabel: "Set due date",
dialogLabel: "Due date selector",
suggestionsLabel: "Due date suggestions",
datePickerLabel: "Task date",
emptyDate: "Due date",
today: "Today",
tomorrow: "Tomorrow",
nextWeek: "Next week",
noDate: "No date",
timeDialog: {
timeLabel: "Time",
saveButtonLabel: "Save",
cancelButtonLabel: "Cancel",
},
},
labelSelector: {
buttonLabel: "Set labels",
buttonText: (num: number) => {
return `Labels (${num})`;
},
labelOptionsLabel: "Label options",
},
prioritySelector: {
buttonLabel: "Set priority",
optionsLabel: "Task priority options",
p1: "Priority 1",
p2: "Priority 2",
p3: "Priority 3",
p4: "Priority 4",
},
projectSelector: {
buttonLabel: "Set project",
selectorLabel: "Project selector",
optionsLabel: "Project options",
search: {
label: "Filter projects",
placeholder: "Type a project name",
},
},
},
onboardingModal: {
failureNoticeMessage: "Failed to save API token",
explainer:
"In order to use this plugin, you must provide your Todoist API token. This allows us to read and write data to or from your Todoist account.",
todoistGuideHint: {
before: "You can follow ",
linkText: "Todoist's guide",
after: " on finding your API token.",
},
tokenInputLabel: "API Token",
submitButtonLabel: "Save",
},
query: {
displays: {
empty: {
label: "The query returned no tasks",
},
error: {
header: "Error",
badRequest:
"The Todoist API has rejected the request. Please check the filter to ensure it is valid.",
unauthorized:
"The Todoist API request is missing or has the incorrect credentials. Please check the API token in the settings.",
unknown:
"Unknown error occurred. Please check the Console in the Developer Tools window for more information",
},
parsingError: {
header: "Error: Query parsing failed",
unknownErrorMessage:
"Unknown error occurred. Please check the Console in the Developer Tools window for more information",
},
},
contextMenu: {
completeTaskLabel: "Complete task",
openTaskInAppLabel: "Open task in Todoist (app)",
openTaskInBrowserLabel: "Open task in Todoist (web)",
},
failedCloseMessage: "Failed to close task",
header: {
errorPostfix: "(Error)",
},
warning: {
header: "Warnings",
},
},
commands: {
sync: "Sync with Todoist",
addTask: "Add task",
addTaskPageContent: "Add task with current page in task content",
addTaskPageDescription: "Add task with current page in task description",
},
tokenValidation: {
emptyTokenError: "API token must not be empty",
invalidTokenError:
"Oops! Todoist does not recognize this token. Please double check and try again!",
},
};
Loading

0 comments on commit c0a8e2d

Please sign in to comment.