diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json
new file mode 100644
index 00000000..6957ca70
--- /dev/null
+++ b/src/i18n/locales/vi.json
@@ -0,0 +1,579 @@
+{
+ "commands": {
+ "checkValidity": {
+ "inBranch": {
+ "error403": "Error 403: {{- repo.owner}}/{{- repo.repo}} was moved permanently (from {{- repo.branch}}).",
+ "error404": "Error 404: The branch {{- repo.branch}} was not found in {{- repo.owner}}/{{- repo.repo}}."
+ },
+ "inRepo": {
+ "error301": "Error 301: {{- repo.owner}}/{{- repo.repo}} was moved permanently.",
+ "error403": "Error 403: this action is forbidden for {{- repo.owner}}/{{- repo.repo}}.",
+ "error404": "Error 404: {{- repo.owner}}/{{- repo.repo}}: is not found."
+ },
+ "rateLimit": {
+ "command": "Check the rate limit of the GitHub API",
+ "limited": "You have reached the rate limit of the GitHub API. The limit will be reset at {{- resetTime}}.",
+ "notLimited": "You are not rate limited. You can make {{- remaining}} requests before the limit is reset at {{- resetTime}}."
+ },
+ "repoExistsTestBranch": "Repository {{- repo.owner}}/{{- repo.repo}} exists. Now testing the {{- repo.branch}} branch.",
+ "success": "{{- repo.owner}}/{{- repo.repo}} seems to be valid!",
+ "title": "Test the connection to the configured repository"
+ },
+ "copyLink": {
+ "onActivation": "Link copied to your clipboard",
+ "title": "Create a link to this note"
+ },
+ "publisherDeleteClean": "Purge depublished and deleted files",
+ "runOtherRepo": {
+ "noFile": "No file is active or the file is not shared",
+ "title": "Run command for a repository"
+ },
+ "shareActiveFile": "Upload single current active note",
+ "shareViewFiles": {
+ "multiple": {
+ "on": "Upload {{- doc}} to {{- smartKey }}",
+ "other": "Upload to…"
+ }
+ },
+ "uploadAllEditedNote": "Refresh all published notes",
+ "uploadAllNewEditedNote": "Refresh published and upload new notes",
+ "uploadAllNotes": "Upload all shared notes",
+ "uploadNewNotes": "Upload unpublished notes"
+ },
+ "common": {
+ "add": "Add {{- things}}",
+ "after": "After",
+ "attachments": "Attachments",
+ "before": "Before",
+ "cancel": "Cancel",
+ "close": "Close",
+ "default": "default",
+ "defaultName": "default folder",
+ "delete": "Delete {{- things}}",
+ "edit": "Edit {{- things}}",
+ "error": "Error",
+ "files": "Files",
+ "ghToken": "GitHub token",
+ "here": "here",
+ "or": "or",
+ "path": {
+ "file": "File name",
+ "folder": "Folder path",
+ "full": "Filepath"
+ },
+ "published": "published",
+ "regex": "regex",
+ "rename": "Renamed key(s)...",
+ "repository": "Repository",
+ "rootFolder": "root folder",
+ "save": "Save",
+ "shared": "shared",
+ "text": "text",
+ "warning": "Warning"
+ },
+ "deletion": {
+ "defaultFolder": "You need a default folder name in the settings to use this command.",
+ "failed": "Failed to delete {{- nb}} files.",
+ "noFile": "No files have been deleted.",
+ "rootFolder": "You need to configure a root folder in the settings to use this command.",
+ "success": "Successfully deleted {{- nb}} files."
+ },
+ "error": {
+ "alreadyExists": "{{- file}} already exists.",
+ "autoClean": "Since {{- what}} is empty, auto-cleaning is disabled.",
+ "dataview": "Unable to render dataview query. Please update the dataview plugin to the last version.",
+ "errorConfig": "Error configuring {{- repo.owner}}/{{- repo.repo}}. Please check your settings.",
+ "errorPublish": "Error during upload to {{- repo.owner}}/{{- repo.repo}}:{{- repo.branch}}",
+ "isEmpty": "{{- what}} is empty.",
+ "mergeconflic": "Pull-request is not mergeable, you need to do it manually.",
+ "normal": "The 404 error is normal ! It means that the file does not exist yet. Don't worry ❤️.",
+ "reading-token-file": "Error: the path seems incorrect.",
+ "unablePublishMultiNotes": "Unable to upload multiple notes, something went wrong.",
+ "unablePublishNote": "Unable to upload note {{- file}}, skipping it",
+ "whatEmpty": {
+ "branch": "Branch",
+ "owner": "Owner"
+ }
+ },
+ "informations": {
+ "foundNoteToSend": "Found {{- nbNotes}} new notes to send",
+ "migrating": {
+ "fileReplace": "Migration of filename replace to the new format...",
+ "normalFormat": "Migrating settings...",
+ "oldSettings": "Migration of old settings to new settings format...",
+ "subFolder": "Adding replacing subfolder to the folderpath replacement..."
+ },
+ "noNewNote": "No new notes to upload.",
+ "scanningRepo": "Scanning the repository, may take a while...",
+ "sendMessage": "Upload {{- nbNotes}} notes to {{- repo.owner}}/{{- repo.repo}}",
+ "startingClean": "Starting cleaning {{- repo.owner}}/{{- repo.repo}}",
+ "successPublishOneNote": "Successfully uploaded {{- file}} to {{- repo.owner}}/{{- repo.repo}}",
+ "successfulPublish": "Successfully uploaded {{- nbNotes}} to {{- repo.owner}}/{{- repo.repo}}",
+ "waitingWorkflow": "Now, waiting for the workflow to be completed..."
+ },
+ "modals": {
+ "export": {
+ "copy": "Copy to clipboard",
+ "desc": "Export settings to clipboard or a file.",
+ "download": "Download",
+ "title": "Export settings"
+ },
+ "import": {
+ "desc": "Import settings from text or a file. Note : this will overwrite your current settings (except for username, repo name and token).",
+ "error": {
+ "isEmpty": "the configuration is empty.",
+ "span": "Error importing configuration: "
+ },
+ "importFromFile": "Import from file",
+ "paste": "Paste configuration here...",
+ "presets": {
+ "desc": "Load presets from the repository \"plugin-presets\"",
+ "title": "Presets"
+ },
+ "title": "Import settings"
+ },
+ "listChangedFiles": {
+ "added": "Added",
+ "deleted": "Deleted",
+ "edited": "Edited",
+ "error": "Errors",
+ "notDeleted": "Cannot be deleted",
+ "title": "List of files edited in the repository",
+ "unpublished": "Cannot be published"
+ }
+ },
+ "publish": {
+ "branch": {
+ "alreadyExists": "Branch already exists ({{- branchName}} on {{- repo.owner}}/{{- repo.repo}} - Using it.",
+ "error": "Error with {{- repo.owner}}/{{- repo.repo}}: {{- error}}",
+ "prMessage": "Pull-Request [{{- branchName}}] from Obsidian",
+ "success": "Branch successfully created (status: {{- branchStatus}}) on {{- repo.owner}}/{{- repo.repo}}"
+ }
+ },
+ "regex": {
+ "entry": "Value to replace",
+ "replace": "Replacement"
+ },
+ "settings": {
+ "conversion": {
+ "dataview": {
+ "desc": "Convert dataview to markdown. Only works if Dataview is enabled.",
+ "title": "Dataview"
+ },
+ "desc": "Theses option won't change the content of the file in your Obsidian Vault, but will change the content of the file in GitHub.",
+ "hardBreak": {
+ "desc": "Add a markdown hard line break (double whitespace) after each line.",
+ "title": "Markdown hard line break"
+ },
+ "links": {
+ "desc": "Put `links: false` in the frontmatter of a note to prevent links to it to be converted and keep the alt text (or filename)",
+ "folderNote": {
+ "desc": "Rename files to a specified name (default: index.md) if it has the same name as their parent folder/category (also works if the note is outside of the folder).",
+ "title": "Folder note"
+ },
+ "internals": {
+ "desc": "Convert internal links to their counterpart in the repository, with relative path.",
+ "shareAll": "Includes all links for the \"share all\" settings, as it impossible without frontmatter to know the sharing state of a file.",
+ "title": "Internals links"
+ },
+ "nonShared": {
+ "desc": "Same option as internals, but for notes that are not yet published. Disabled, only the filename will be conserved.",
+ "title": "Convert internal links pointing to unpublished notes"
+ },
+ "slugify": {
+ "desc": "Standardize the slug of anchor links (pointing to heading title). Transform the slug into all lower case. Replace space with hyphen. Applicable only for anchor links in markdown link syntax.",
+ "disable": "Disable",
+ "lower": "",
+ "strict": "Convert all to alphanumeric and dashes, including unicode and non latin languages.",
+ "title": "Sluglify anchor in markdown links"
+ },
+ "title": "Links",
+ "wikilinks": {
+ "desc": "Convert Wikilinks to MDlinks, without changing the contents.",
+ "title": "[[Wikilinks]] to [MDlinks](links)"
+ }
+ },
+ "sectionTitle": "Main text",
+ "tags": {
+ "desc": "This will convert any properties or dataview inline field into properties tags. Separate fields with a comma.",
+ "exclude": {
+ "desc": "This will exclude value from being converted. Separate fields with a comma.",
+ "placeholder": "Field value",
+ "title": "Exclude value from conversion"
+ },
+ "inlineTags": {
+ "desc": "Add your inline tags in your properties tags field and converting nested tags with replacing \"/\" with \"_\"",
+ "title": "Inline tags"
+ },
+ "title": "Convert properties/dataview fields into tags"
+ },
+ "title": "Content"
+ },
+ "embed": {
+ "attachment": "Attachments",
+ "bake": {
+ "text": "Allow you to add text before and after each embed, for example adding HTML or stylize the block with markdown.",
+ "textAfter": {
+ "title": "Text after the block"
+ },
+ "textBefore": {
+ "title": "Text before the block"
+ },
+ "title": "Include embed settings",
+ "variable": {
+ "desc": "It is possible to use the following variable:",
+ "title": ": note embedded title",
+ "url": ": path to the embedded note"
+ },
+ "warning": "If you use HTML, depending of your host settings, the markdown will broke."
+ },
+ "char": {
+ "desc": "Character(s) to add before the link.",
+ "title": "Embed characters"
+ },
+ "defaultImageFolder": {
+ "desc": "To use a folder different from default",
+ "title": "Default attachment folder"
+ },
+ "forcePush": {
+ "all": "Use {{all}} to change the destination of all attachments (and/or force them to be sent).",
+ "default": "Use {{default}} to use the default destination path.",
+ "desc": "The following extensions will always be published, regardless of the last sending date.",
+ "info": "By default, attachments are only sent if they have been modified since they were last sent, or if they do not exist in the repository.",
+ "separateByComma": "Separate extensions with commas. \nYou can use regex by enclosing it with \"/\", like /regex/. Use {{all}} to force all attachments to be sent.",
+ "title": "Force attachments to be sent"
+ },
+ "imagePath": {
+ "desc": "Use the obsidian folder structure or configure a default folder below",
+ "title": "Structure"
+ },
+ "links": {
+ "desc": "Allow to edit the links of the embeds, removing entirely the citation, or transform to a simple link",
+ "dp": {
+ "bake": "Include embed contents",
+ "keep": "No change",
+ "links": "Transform to link",
+ "remove": "Remove link completely"
+ },
+ "title": "Change embed markup"
+ },
+ "notes": "Embed notes",
+ "overrides": {
+ "desc": "Allow to send an attachment into a specific path and force push attachments.",
+ "modal": {
+ "dest": "Destination",
+ "path": "Path or extension",
+ "title": "Override attachments path"
+ }
+ },
+ "title": "Attachment & embeds ",
+ "transferImage": {
+ "title": "Transfer attachments"
+ },
+ "transferMetaFile": {
+ "desc": "Set the names of the metadata field you want to use to send files. Separate fields with a comma. Dataview inline field are supported.",
+ "title": "Send files using a metadata field"
+ },
+ "transferNotes": {
+ "desc": "Send embedded notes in a shared file to GitHub. Only shared files will be send!",
+ "title": "Transfer embedded notes"
+ }
+ },
+ "github": {
+ "apiType": {
+ "desc": "Choose between the GitHub API or the GitHub Enterprise API (only GitHub Enterprise users — Advanced user!).",
+ "dropdown": {
+ "enterprise": "Enterprise",
+ "free": "Free/Pro/Team (default)"
+ },
+ "hostname": {
+ "desc": "The hostname of your GitHub Enterprise instance.",
+ "title": "GitHub Enterprise Hostname"
+ },
+ "title": "API Type"
+ },
+ "automaticallyMergePR": "Automatically merge pull requests",
+ "branch": {
+ "desc": "If you use a different branch than \"main\"",
+ "title": "Main branch"
+ },
+ "dryRun": {
+ "enable": {
+ "desc": "Disable GitHub push and all other action and only perform a dry-run to see what would be pushed or deleted in the repository.",
+ "title": "Test mode"
+ },
+ "folder": {
+ "desc": "Use {{owner}}, {{repo}} and {{branch}} to dynamically name the folder.",
+ "title": "Folder where the repository will be simulated (test mode only)"
+ }
+ },
+ "ghToken": {
+ "button": {
+ "configDir": ": The configuration folder of Obsidian",
+ "default": "By default, it will be in:",
+ "description": "You can edit the file path where the token will be stored.",
+ "pluginID": ": The plugin ID",
+ "tooltip": "Modify the path of the file containing the token",
+ "variables": "You can use the following variables:"
+ },
+ "desc": "A GitHub token with repository permission. You can generate it ",
+ "error": "The token mustn't be empty!"
+ },
+ "repoName": {
+ "desc": "The name of the repository where you store your blog.",
+ "placeholder": "mkdocs-template",
+ "title": "Repository name"
+ },
+ "smartRepo": {
+ "button": "Manage more repository",
+ "modals": {
+ "default": "The name \"default\" is reserved for your primary configuration.",
+ "desc": "These \"other repositories\" allow you to use all the commands on the repositories added below.",
+ "duplicate": "Smartkey must be unique!",
+ "empty": "Smartkeys cannot be empty",
+ "frontmatterInfo": "Using the \"shortRepo\" properties key with the \"smartKey\" also allows you to use this repository in a file without having to write its path.",
+ "newRepo": "a new repository",
+ "otherConfig": "Other settings",
+ "shortcuts": {
+ "desc": "Add all commands for this repository in the command palette",
+ "title": "Shortcuts"
+ },
+ "title": "Manage others repositories"
+ }
+ },
+ "testConnection": "Test connection",
+ "title": "GitHub config",
+ "username": {
+ "desc": "The username or organization hosting the repo in GitHub",
+ "title": "GitHub username"
+ }
+ },
+ "githubWorkflow": {
+ "autoCleanUp": {
+ "desc": "Remove depublished files (stopped sharing or deleted) from GitHub",
+ "title": "Auto clean up"
+ },
+ "excludedFiles": {
+ "desc": "If you want to exclude some folder or file from the autoclean, add them here. You can use regex by surrounding the string with \"/\". Separate files with a comma.",
+ "title": "Excluded files and folder"
+ },
+ "githubAction": {
+ "desc": "If you want to activate a GitHub action when the plugin push the file, set the name of the file (in your .github/worfklows folder). Only workflow with the \"workflow_dispatch\" event will be triggered.",
+ "title": "GitHub action name"
+ },
+ "prRequest": {
+ "desc": "The message send when the pull-request is merged. Will always followed by the pull-request number.",
+ "error": "You can't use an empty string here!",
+ "title": "Commit message"
+ },
+ "useMetadataExtractor": {
+ "desc": "Send the files generated by the metadata-extractor plugin in this folder.",
+ "title": "Metadata-extractor files"
+ }
+ },
+ "help": {
+ "frontmatter": {
+ "attachment": {
+ "folder": "Change the default folder for the attachments",
+ "send": "Send all attachments to GitHub"
+ },
+ "autoclean": "Disable or enable autocleaning",
+ "baselink": {
+ "desc": "Change the base link for the copy link command. Also disable the link replacer part. Can be used as an properties object with the name ",
+ "remove": "Remove part of the link. It must be a list!"
+ },
+ "convert": {
+ "enableOrDisable": "Enable or disable the conversion of links. Disabling this will remove the",
+ "syntax": "syntax, while keeping the file name or the alternative text."
+ },
+ "dataview": "Convert dataview queries to markdown.",
+ "desc": "Moreover, there are some properties keys that can be useful for your workflow. The code below show the default settings, but feel free to change it to your needs in each notes!",
+ "embed": {
+ "char": "Add a character(s) before the embedded links. Used only if you set \"remove\" to \"links\".",
+ "remove": {
+ "bake": "Include the content of the embed (support blocks, heading and entire file)",
+ "desc": "Modify the aspect of the embedded files link. Can take the followed value:",
+ "keep": "Leave as in Obsidian",
+ "links": "Convert to links (delete or edit the \"!\")",
+ "remove": "Delete the citation completely and leave an empty line"
+ },
+ "send": "Send embedded note to GitHub"
+ },
+ "hardBreak": "Convert all linebreaks to markdown «hard break».",
+ "internals": "Convert internals links to their counterpart in the website, with relative path. Disabled, the plugin will keep the internal link as is.",
+ "mdlinks": "Convert all [[wikilinks]] to [markdown](links)",
+ "nonShared": "Convert internal links pointing to a unshared file to their counterpart in the website, with relative path. Disabled, the plugin will keep the filename.",
+ "path": "You can override all path settings using this key. The path will be relative to the root of your repository.",
+ "repo": {
+ "branch": "Branch of the repo",
+ "desc": "Change the default repo for the note.",
+ "owner": "Owner of the repo"
+ },
+ "share": {
+ "other": "You could also use another shareKey based on the key set in « Manage other repo ». It allows you to separate your different repository. If the main and secondaries key are used, the main repo will be used.",
+ "title": "This key is used to share a note with the plugin."
+ },
+ "shortRepo": "Allow to use one of the repo set in other repo settings.",
+ "title": "Property key cheatsheet",
+ "titleKey": "Change the title of the note."
+ },
+ "multiRepoHelp": {
+ "desc": "If you want to send your notes to multiple repository, you can use the ",
+ "desc2": "key in your properties. The value of this key must be a list of repository. Each repository must have the following keys ",
+ "exampleDesc": "The code below show an example based on your settings.",
+ "title": "Send to multiple repository"
+ },
+ "title": "Help",
+ "usefulLinks": {
+ "discussion": "Discussion",
+ "documentation": "Documentation",
+ "issue": "Issue",
+ "links": "https://obsidian-publisher.netlify.app/",
+ "title": "Useful links"
+ }
+ },
+ "overrides": {},
+ "plugin": {
+ "copyLink": {
+ "baselink": {
+ "desc": "The base link of your website. By default : https://username.github.io/repo/",
+ "title": "Base link"
+ },
+ "command": {
+ "desc": "Add a command to copy the link of the note (need reloading the plugin to take effect)"
+ },
+ "desc": "Send a link to your note in your clipboard",
+ "linkPathRemover": {
+ "desc": "Delete this part of the links created. Separate with a comma if several values are to be deleted.",
+ "title": "Deleting part of the link"
+ },
+ "title": "Copy link"
+ },
+ "dev": {
+ "desc": "Advanced user only. Will display as notice according to the previous setting.",
+ "title": "Display developper logs"
+ },
+ "editorMenu": {
+ "desc": "Add a sharing command in the right-click menu",
+ "title": "Editor menu"
+ },
+ "embedEditRepo": {
+ "desc": "Display a modal how the new version differs",
+ "title": "Show what files are edited, added, or deleted after uploaded"
+ },
+ "excludedFolder": {
+ "desc": "Never publish file in these folders, regardless of the share key. Read the docs for more info.",
+ "title": "Excluded folders"
+ },
+ "fileMenu": {
+ "desc": "Add a sharing command in the file menu",
+ "title": "File menu"
+ },
+ "head": {
+ "copyLinks": "Link building & copy",
+ "log": "Logs",
+ "menu": "Menu",
+ "other": "Others",
+ "share": "Sharing config"
+ },
+ "logNoticeHeader": {
+ "desc": "On mobile, it can be hard to debug the plugin. Enable this option to log every error in a Notice.",
+ "title": "Notice every error"
+ },
+ "saveTab": {
+ "desc": "Allows you to reopen the settings on the previously used tab",
+ "title": "Save tab"
+ },
+ "set": {
+ "desc": "Choose the property key you want to use to link the property of a file to another, without rewrite them each time. Work only for file linked by a wikilink in the frontmatter.",
+ "title": "Set of options"
+ },
+ "shareKey": {
+ "all": {
+ "desc": "Share all files regardless of the state of the share key of the notes",
+ "title": "Share all files"
+ },
+ "desc": "The YAML frontmatter key to publish your file on the website. Default is `share`.",
+ "excludedFileName": {
+ "title": "Exclude files with names starting with ..."
+ },
+ "otherRepo": "You can also define a share key to separate with others, without using the shortRepo key.",
+ "title": "Share key"
+ },
+ "title": "Plugin settings"
+ },
+ "regexReplacing": {
+ "empty": "Replacement can be empty to remove the whole string.",
+ "emptyRegex": "The value to replace cannot be empty",
+ "forbiddenValue": "The {{- what}} cannont contain the character: {{- forbiddenChar}}",
+ "inCodeBlocks": {
+ "runIn": "Runned in code-blocks",
+ "runOut": "Not runned in the code blocks"
+ },
+ "invalidRegex": "An error has occurred: {{- e}}",
+ "modal": {
+ "desc": "Replace text in the file with the given value. Enclose the text with \"//\" to use regex.",
+ "force": "Force push",
+ "keywords": "Keywords",
+ "name": "Use {{name}} to use the filename.",
+ "title": {
+ "all": "Folder path & filename replacer",
+ "only": "Replace filename",
+ "text": "Text replacer"
+ }
+ },
+ "momentReplaceRegex": "Run replacement {{- moment}} the other plugin conversion (dataview, internals links...)",
+ "warningPath": "Warning! Using the character \"/\" will edit the path, be careful with this option."
+ },
+ "upload": {
+ "defaultFolder": {
+ "desc": "Set the default reception folder. Leave it empty to use the root of the repository.",
+ "other": "Use this folder as root within the repository (each path will be prepend by it)",
+ "placeholder": "docs",
+ "title": "Default folder"
+ },
+ "folderBehavior": {
+ "desc": "Choose the file tree in the repository, with using a property key, a fixed folder or your Obsidian file tree.",
+ "fixedFolder": "Fixed Folder",
+ "obsidianPath": "Obsidian Path",
+ "title": "File tree in repository",
+ "yaml": "Property key"
+ },
+ "folderNote": {
+ "addTitle": {
+ "key": "Use a key other than \"title\"",
+ "title": "Automatically add the \"title\" key with the file name"
+ }
+ },
+ "frontmatterKey": {
+ "desc": "Set the key where to get the folder's value.",
+ "placeholder": "category",
+ "title": "Property key"
+ },
+ "regexFilePathTitle": {
+ "desc": "If the text is between \"//\", it will be used as a regex. Otherwise, it will be used as a string.",
+ "title": {
+ "FolderPathTitle": "Apply edit on the folder path or the filename (automatically)",
+ "titleOnly": "Apply edit on the filename (automatically)"
+ }
+ },
+ "rootFolder": {
+ "desc": "Append this path to the folder set by the properties key",
+ "other": "Use this folder as root within the repository (each path will be prepend by it).",
+ "title": "Root folder"
+ },
+ "title": "File paths",
+ "useFrontmatterTitle": {
+ "desc": "Use a property value to generate the filename. By default, \"title\" is used. ",
+ "title": "Set the key where to get the value of the filename"
+ }
+ }
+ },
+ "statusBar": {
+ "counter": "{{- msg}}: {{- counter}}/{{- nb}}",
+ "loading": "Loading...",
+ "markedForSharing": "{{- nb}} {{- type}} marked for sharing",
+ "sharing": "Sharing {{- type}}",
+ "success": "{{- type}} {{- action}}"
+ }
+}