-
-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add i18n framework for defining and using translations
- Loading branch information
1 parent
2ab07c6
commit c0a8e2d
Showing
4 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>> = {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!", | ||
}, | ||
}; |
Oops, something went wrong.