1
1
mirror of https://github.com/Eugeny/tabby.git synced 2024-11-22 11:52:03 +03:00

Translation infrastructure

This commit is contained in:
Eugene Pankov 2022-01-08 16:02:56 +01:00
parent 04010b58bb
commit 0814d44207
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
134 changed files with 8137 additions and 889 deletions

2
.gitignore vendored
View File

@ -34,3 +34,5 @@ sentry-symbols.js
tabby-ssh/util/pagent.exe tabby-ssh/util/pagent.exe
*.psd *.psd
crowdin.yml

3
locale/STOP.txt Normal file
View File

@ -0,0 +1,3 @@
Do not submit pull requests for translations.
Translations are managed at https://crowdin.com/project/tabby

196
locale/af.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: af\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Afrikaans\n"
"Language: af_ZA\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

1303
locale/app.pot Normal file

File diff suppressed because it is too large Load Diff

196
locale/ar.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Arabic\n"
"Language: ar_SA\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/ca.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: ca\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Catalan\n"
"Language: ca_ES\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/cs.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: cs\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Czech\n"
"Language: cs_CZ\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/da.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: da\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Danish\n"
"Language: da_DK\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/de.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: German\n"
"Language: de_DE\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/el.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: el\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Greek\n"
"Language: el_GR\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/en.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: en\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: English\n"
"Language: en_US\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/es.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/fi.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: fi\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Finnish\n"
"Language: fi_FI\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/fr.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: French\n"
"Language: fr_FR\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/he.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: he\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Hebrew\n"
"Language: he_IL\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/hu.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: hu\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Hungarian\n"
"Language: hu_HU\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/it.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Italian\n"
"Language: it_IT\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/ja.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Japanese\n"
"Language: ja_JP\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/ko.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Korean\n"
"Language: ko_KR\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/nl.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/no.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: no\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Norwegian\n"
"Language: no_NO\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/pl.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Polish\n"
"Language: pl_PL\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/pt.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/ro.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: ro\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Romanian\n"
"Language: ro_RO\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/ru.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr "Приложение"
msgid "Application settings"
msgstr "Настройки приложения"
msgid "Ask a question"
msgstr "Задать вопрос"
msgid "Automatic Updates"
msgstr "Автоматическое обновление"
msgid "Blue"
msgstr "Синий"
msgid "Check for updates"
msgstr "Проверить обновления"
msgid "Close"
msgstr "Закрыть"
msgid "Close other tabs"
msgstr "Закрыть другие вкладки"
msgid "Close tabs to the left"
msgstr "Закрыть вкладки слева"
msgid "Close tabs to the right"
msgstr "Закрыть вкладки справа"
msgid "Color"
msgstr "Цвет"
msgid "Config file"
msgstr "Файл настроек"
msgid "Current process: {name}"
msgstr "Текущий процесс: {name}"
msgid "Debugging"
msgstr "Отладка"
msgid "Defaults"
msgstr "Значения по умолчанию"
msgid "Down"
msgstr "Вниз"
msgid "Duplicate"
msgstr "Дублировать"
msgid "Enable analytics"
msgstr "Аналитика"
msgid "Enable automatic installation of updates when they become available."
msgstr "Включить автоматическую установку обновлений, когда они доступны."
msgid "English"
msgstr "Английский"
msgid "French"
msgstr "Французский"
msgid "Generate a pre-filled GitHub issue"
msgstr "Открыть пред-заполненный отчет на GitHub"
msgid "German"
msgstr "Немецкий"
msgid "Green"
msgstr "Зеленый"
msgid "Invalid syntax"
msgstr "Неверный синтаксис"
msgid "Language"
msgstr "Язык"
msgid "Left"
msgstr "Налево"
msgid "No color"
msgstr "Без цвета"
msgid "Notify on activity"
msgstr "Уведомить об активности"
msgid "Notify when done"
msgstr "Уведомить о завершении"
msgid "On GitHub Discussions"
msgstr "На GitHub Discussions"
msgid "Open DevTools"
msgstr "Открыть DevTools"
msgid "Orange"
msgstr "Оранжевый"
msgid "Process completed"
msgstr "Процесс завершен"
msgid "Profile name"
msgstr "Название профиля"
msgid "Purple"
msgstr "Фиолетовый"
msgid "Red"
msgstr "Красный"
msgid "Rename"
msgstr "Переименовать"
msgid "Report a problem"
msgstr "Сообщить о проблеме"
msgid "Restart the app to apply changes"
msgstr "Перезапустите приложение, чтобы применить изменения"
msgid "Right"
msgstr "Направо"
msgid "Russian"
msgstr "Русский"
msgid "Save and apply"
msgstr "Сохранить и применить"
msgid "Save layout as profile"
msgstr "Сохранить как профиль"
msgid "Shell integration"
msgstr "Интеграция в систему"
msgid "Show config file"
msgstr "Показать файл настроек"
msgid "Show defaults"
msgstr "Показать значения по умолчанию"
msgid "Show release notes"
msgstr "Посмотреть изменения в релизе"
msgid "Source code"
msgstr "Исходный код"
msgid "Split"
msgstr "Разделить"
msgid "Subscribe to updates"
msgstr "Подпишитесь на обновления"
msgid "Tab activity"
msgstr "Активность в вкладке"
msgid "Tabby news and updates on Twitter"
msgstr "Новости и обновления про Tabby в Twitter"
msgid "Up"
msgstr "Вверх"
msgid "Update"
msgstr "Обновить"
msgid "We're only tracking your Tabby and OS versions."
msgstr "Отслеживаем только версию ОС и приложения."
msgid "What's new"
msgstr "Что нового"
msgid "Yellow"
msgstr "Желтый"
msgid "click"
msgstr ""

196
locale/sr.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: sr\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Serbian (Cyrillic)\n"
"Language: sr_SP\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/sv.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: sv-SE\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Swedish\n"
"Language: sv_SE\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/tr.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/uk.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/vi.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: vi\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Vietnamese\n"
"Language: vi_VN\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

196
locale/zh.po Normal file
View File

@ -0,0 +1,196 @@
msgid ""
msgstr ""
"mime-version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Crowdin-Project: tabby\n"
"X-Crowdin-Project-ID: 493349\n"
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /locale/app.pot\n"
"X-Crowdin-File-ID: 75\n"
"Project-Id-Version: tabby\n"
"Language-Team: Chinese Traditional\n"
"Language: zh_TW\n"
"PO-Revision-Date: 2022-01-08 12:42\n"
msgid "Allows quickly opening a terminal in the selected folder"
msgstr ""
msgid "Application"
msgstr ""
msgid "Application settings"
msgstr ""
msgid "Ask a question"
msgstr ""
msgid "Automatic Updates"
msgstr ""
msgid "Blue"
msgstr ""
msgid "Check for updates"
msgstr ""
msgid "Close"
msgstr ""
msgid "Close other tabs"
msgstr ""
msgid "Close tabs to the left"
msgstr ""
msgid "Close tabs to the right"
msgstr ""
msgid "Color"
msgstr ""
msgid "Config file"
msgstr ""
msgid "Current process: {name}"
msgstr ""
msgid "Debugging"
msgstr ""
msgid "Defaults"
msgstr ""
msgid "Down"
msgstr ""
msgid "Duplicate"
msgstr ""
msgid "Enable analytics"
msgstr ""
msgid "Enable automatic installation of updates when they become available."
msgstr ""
msgid "English"
msgstr ""
msgid "French"
msgstr ""
msgid "Generate a pre-filled GitHub issue"
msgstr ""
msgid "German"
msgstr ""
msgid "Green"
msgstr ""
msgid "Invalid syntax"
msgstr ""
msgid "Language"
msgstr ""
msgid "Left"
msgstr ""
msgid "No color"
msgstr ""
msgid "Notify on activity"
msgstr ""
msgid "Notify when done"
msgstr ""
msgid "On GitHub Discussions"
msgstr ""
msgid "Open DevTools"
msgstr ""
msgid "Orange"
msgstr ""
msgid "Process completed"
msgstr ""
msgid "Profile name"
msgstr ""
msgid "Purple"
msgstr ""
msgid "Red"
msgstr ""
msgid "Rename"
msgstr ""
msgid "Report a problem"
msgstr ""
msgid "Restart the app to apply changes"
msgstr ""
msgid "Right"
msgstr ""
msgid "Russian"
msgstr ""
msgid "Save and apply"
msgstr ""
msgid "Save layout as profile"
msgstr ""
msgid "Shell integration"
msgstr ""
msgid "Show config file"
msgstr ""
msgid "Show defaults"
msgstr ""
msgid "Show release notes"
msgstr ""
msgid "Source code"
msgstr ""
msgid "Split"
msgstr ""
msgid "Subscribe to updates"
msgstr ""
msgid "Tab activity"
msgstr ""
msgid "Tabby news and updates on Twitter"
msgstr ""
msgid "Up"
msgstr ""
msgid "Update"
msgstr ""
msgid "We're only tracking your Tabby and OS versions."
msgstr ""
msgid "What's new"
msgstr ""
msgid "Yellow"
msgstr ""
msgid "click"
msgstr ""

View File

@ -7,6 +7,8 @@
"@angular/forms": "^12.0.0", "@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0", "@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0", "@angular/platform-browser-dynamic": "^12.0.0",
"@biesbjerg/ngx-translate-extract": "^7.0.4",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
"@fortawesome/fontawesome-free": "^6.0.0-beta3", "@fortawesome/fontawesome-free": "^6.0.0-beta3",
"@ng-bootstrap/ng-bootstrap": "^10.0.0", "@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@sentry/cli": "^1.71.0", "@sentry/cli": "^1.71.0",
@ -40,7 +42,7 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"graceful-fs": "^4.2.8", "graceful-fs": "^4.2.8",
"html-loader": "2.1.2", "html-loader": "2.1.2",
"json-loader": "0.5.7", "json-loader": "^0.5.7",
"lru-cache": "^6.0.0", "lru-cache": "^6.0.0",
"macos-release": "^3.0.1", "macos-release": "^3.0.1",
"ngx-sortablejs": "^11.1.0", "ngx-sortablejs": "^11.1.0",
@ -50,7 +52,9 @@
"npmlog": "6.0.0", "npmlog": "6.0.0",
"npx": "^10.2.2", "npx": "^10.2.2",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
"pug": "^3.0.2", "po-gettext-loader": "^1.0.0",
"pug": "3",
"pug-cli": "^1.0.0-alpha6",
"pug-html-loader": "1.1.5", "pug-html-loader": "1.1.5",
"pug-lint": "^2.6.0", "pug-lint": "^2.6.0",
"pug-loader": "^2.4.0", "pug-loader": "^2.4.0",
@ -80,6 +84,7 @@
"zone.js": "^0.11.4" "zone.js": "^0.11.4"
}, },
"resolutions": { "resolutions": {
"*/pug": "^3",
"lzma-native": "^8.0.0", "lzma-native": "^8.0.0",
"*/node-abi": "^3.5.0", "*/node-abi": "^3.5.0",
"**/graceful-fs": "^4.2.4" "**/graceful-fs": "^4.2.4"

28
scripts/i18n-extract.js Executable file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env node
const sh = require('shelljs')
const fs = require('fs/promises')
const vars = require('./vars')
const log = require('npmlog')
const tempOutput = 'locale/app.new.pot'
const pot = 'locale/app.pot'
const tempHtml = 'locale/tmp-html'
;(async () => {
sh.mkdir('-p', tempHtml)
for (const plugin of vars.builtinPlugins) {
log.info('extract-pug', plugin)
sh.exec(`yarn pug --doctype html -s --pretty -O '{require: function(){}}' -o ${tempHtml}/${plugin} ${plugin}`)
log.info('extract-ts', plugin)
sh.exec(`node node_modules/.bin/ngx-translate-extract -i ${plugin}/src -m -s -f pot -o ${tempOutput}`)
}
log.info('extract-pug')
sh.exec(`node node_modules/.bin/ngx-translate-extract -i ${tempHtml} -f pot -s -o ${tempOutput}`)
sh.rm('-r', tempHtml)
await fs.rename(tempOutput, pot)
})()

View File

@ -17,12 +17,15 @@
"author": "Eugene Pankov", "author": "Eugene Pankov",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@ngx-translate/core": "^14.0.0",
"bootstrap": "^4.1.3", "bootstrap": "^4.1.3",
"deepmerge": "^4.1.1", "deepmerge": "^4.1.1",
"js-yaml": "^4.0.0", "js-yaml": "^4.0.0",
"messageformat": "^2.3.0",
"mixpanel": "^0.13.0", "mixpanel": "^0.13.0",
"ngx-filesize": "^2.0.16", "ngx-filesize": "^2.0.16",
"ngx-perfect-scrollbar": "^10.1.0", "ngx-perfect-scrollbar": "^10.1.0",
"ngx-translate-messageformat-compiler": "^4.11.0",
"readable-stream": "3.6.0", "readable-stream": "3.6.0",
"uuid": "^8.0.0" "uuid": "^8.0.0"
}, },

View File

@ -35,4 +35,5 @@ export { TabsService, NewTabParameters, TabComponentType } from '../services/tab
export { UpdaterService } from '../services/updater.service' export { UpdaterService } from '../services/updater.service'
export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE, StoredVault, VaultSecretKey } from '../services/vault.service' export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE, StoredVault, VaultSecretKey } from '../services/vault.service'
export { FileProvidersService } from '../services/fileProviders.service' export { FileProvidersService } from '../services/fileProviders.service'
export { LocaleService, TranslateServiceWrapper as TranslateService } from '../services/locale.service'
export * from '../utils' export * from '../utils'

View File

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider' import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
import { HostAppService, Platform } from './api/hostApp' import { HostAppService, Platform } from './api/hostApp'
@ -12,6 +13,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
constructor ( constructor (
private hostApp: HostAppService, private hostApp: HostAppService,
private profilesService: ProfilesService, private profilesService: ProfilesService,
private translate: TranslateService,
hotkeys: HotkeysService, hotkeys: HotkeysService,
) { ) {
super() super()
@ -35,7 +37,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
icon: this.hostApp.platform === Platform.Web icon: this.hostApp.platform === Platform.Web
? require('./icons/plus.svg') ? require('./icons/plus.svg')
: require('./icons/profiles.svg'), : require('./icons/profiles.svg'),
title: 'Profiles and connections', title: this.translate.instant('Profiles and connections'),
click: () => this.activate(), click: () => this.activate(),
}, },
...this.profilesService.getRecentProfiles().map(profile => ({ ...this.profilesService.getRecentProfiles().map(profile => ({

View File

@ -2,5 +2,5 @@
input.form-control(type='text', #input, [(ngModel)]='value', (keyup.enter)='save()', autofocus) input.form-control(type='text', #input, [(ngModel)]='value', (keyup.enter)='save()', autofocus)
.modal-footer .modal-footer
button.btn.btn-primary((click)='save()') Save button.btn.btn-primary((click)='save()', translate) Save
button.btn.btn-secondary((click)='close()') Cancel button.btn.btn-secondary((click)='close()', translate) Cancel

View File

@ -1,7 +1,7 @@
.modal-body .modal-body
.alert.alert-danger Tabby could not start with your plugins, so all third party plugins have been disabled in this session. The error was: .alert.alert-danger(translate) Tabby could not start with your plugins, so all third party plugins have been disabled in this session. The error was:
pre {{error}} pre {{error}}
.modal-footer .modal-footer
button.btn.btn-primary((click)='close()') Close button.btn.btn-primary((click)='close()', translate) Close

View File

@ -15,9 +15,9 @@ footer.d-flex.align-items-center
.btn-group.mr-auto .btn-group.mr-auto
button.btn.btn-dark((click)='homeBase.openGitHub()') button.btn.btn-dark((click)='homeBase.openGitHub()')
i.fab.fa-github i.fab.fa-github
span GitHub span(translate) GitHub
button.btn.btn-dark((click)='homeBase.reportBug()') button.btn.btn-dark((click)='homeBase.reportBug()')
i.fas.fa-bug i.fas.fa-bug
span Report a problem span(translate) Report a problem
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}} .form-control-static.selectable.no-drag(translate, [translateParams]='{version: homeBase.appVersion}') Version: {version}

View File

@ -1,5 +1,5 @@
.d-flex.align-items-center .d-flex.align-items-center
.dropdown-header File transfers .dropdown-header(translate) File transfers
button.btn.btn-link.ml-auto((click)='removeAll(); $event.stopPropagation()') !{require('../icons/times.svg')} button.btn.btn-link.ml-auto((click)='removeAll(); $event.stopPropagation()') !{require('../icons/times.svg')}
.transfer(*ngFor='let transfer of transfers', (click)='showTransfer(transfer)') .transfer(*ngFor='let transfer of transfers', (click)='showTransfer(transfer)')
.icon(*ngIf='isDownload(transfer)') !{require('../icons/download.svg')} .icon(*ngIf='isDownload(transfer)') !{require('../icons/download.svg')}

View File

@ -1,4 +1,5 @@
import { Component, Input, Output, EventEmitter } from '@angular/core' import { Component, Input, Output, EventEmitter } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { FileDownload, FileTransfer, PlatformService } from '../api/platform' import { FileDownload, FileTransfer, PlatformService } from '../api/platform'
/** @hidden */ /** @hidden */
@ -11,7 +12,10 @@ export class TransfersMenuComponent {
@Input() transfers: FileTransfer[] @Input() transfers: FileTransfer[]
@Output() transfersChange = new EventEmitter<FileTransfer[]>() @Output() transfersChange = new EventEmitter<FileTransfer[]>()
constructor (private platform: PlatformService) { } constructor (
private platform: PlatformService,
private translate: TranslateService,
) { }
isDownload (transfer: FileTransfer): boolean { isDownload (transfer: FileTransfer): boolean {
return transfer instanceof FileDownload return transfer instanceof FileDownload
@ -40,8 +44,11 @@ export class TransfersMenuComponent {
if (this.transfers.some(x => !x.isComplete())) { if (this.transfers.some(x => !x.isComplete())) {
if ((await this.platform.showMessageBox({ if ((await this.platform.showMessageBox({
type: 'warning', type: 'warning',
message: 'There are active file transfers', message: this.translate.instant('There are active file transfers'),
buttons: ['Abort all', 'Do not abort'], buttons: [
this.translate.instant('Abort all'),
this.translate.instant('Do not abort'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
})).response === 1) { })).response === 1) {

View File

@ -1,13 +1,18 @@
.modal-body .modal-body
.d-flex.align-items-center.mb-3 .d-flex.align-items-center.mb-3
h3.m-0 Vault is locked h3.m-0(translate) Vault is locked
.ml-auto(ngbDropdown, placement='bottom-right') .ml-auto(ngbDropdown, placement='bottom-right')
button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()') button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()')
span(*ngIf='rememberFor') Remember for {{getRememberForDisplay(rememberFor)}} span(
span(*ngIf='!rememberFor') Do not remember *ngIf='rememberFor',
translate,
[translateParams]='{time: getRememberForDisplay(rememberFor)}'
) Remember for {time}
span(*ngIf='!rememberFor', translate) Do not remember
div(ngbDropdownMenu) div(ngbDropdownMenu)
button.dropdown-item( button.dropdown-item(
(click)='rememberFor = 0', (click)='rememberFor = 0',
translate
) Do not remember ) Do not remember
button.dropdown-item( button.dropdown-item(
*ngFor='let x of rememberOptions', *ngFor='let x of rememberOptions',

View File

@ -4,21 +4,21 @@
h1.tabby-title Tabby h1.tabby-title Tabby
sup α sup α
.text-center.mb-5 Thank you for downloading Tabby! .text-center.mb-5(translate) Thank you for downloading Tabby!
.form-line .form-line
.header .header
.title Enable analytics .title(translate) Enable analytics
.description Help track the number of Tabby installs across the world! .description(translate) Help track the number of Tabby installs across the world!
toggle([(ngModel)]='config.store.enableAnalytics') toggle([(ngModel)]='config.store.enableAnalytics')
.form-line .form-line
.header .header
.title Enable global hotkey (#[strong Ctrl-Space]) .title(translate) Enable global hotkey (Ctrl-Space)
.description Toggles the Tabby window visibility .description(translate) Toggles the Tabby window visibility
toggle([(ngModel)]='enableGlobalHotkey') toggle([(ngModel)]='enableGlobalHotkey')
.text-center.mt-5 .text-center.mt-5
button.btn.btn-primary((click)='closeAndDisable()') Close and never show again button.btn.btn-primary((click)='closeAndDisable()', translate) Close and never show again

View File

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { BaseTabComponent } from './baseTab.component' import { BaseTabComponent } from './baseTab.component'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
import { HostWindowService } from '../api/hostWindow' import { HostWindowService } from '../api/hostWindow'
@ -16,9 +17,10 @@ export class WelcomeTabComponent extends BaseTabComponent {
constructor ( constructor (
private hostWindow: HostWindowService, private hostWindow: HostWindowService,
public config: ConfigService, public config: ConfigService,
translate: TranslateService,
) { ) {
super() super()
this.setTitle('Welcome') this.setTitle(translate.instant('Welcome'))
} }
async closeAndDisable () { async closeAndDisable () {

View File

@ -38,3 +38,4 @@ enableExperimentalFeatures: false
pluginBlacklist: [] pluginBlacklist: []
hacks: hacks:
disableGPU: false disableGPU: false
language: null

View File

@ -1,4 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ProfilesService } from './services/profiles.service' import { ProfilesService } from './services/profiles.service'
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider' import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
import { PartialProfile, Profile } from './api' import { PartialProfile, Profile } from './api'
@ -9,188 +10,189 @@ export class AppHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [ hotkeys: HotkeyDescription[] = [
{ {
id: 'profile-selector', id: 'profile-selector',
name: 'Show profile selector', name: this.translate.instant('Show profile selector'),
}, },
{ {
id: 'toggle-fullscreen', id: 'toggle-fullscreen',
name: 'Toggle fullscreen mode', name: this.translate.instant('Toggle fullscreen mode'),
}, },
{ {
id: 'rename-tab', id: 'rename-tab',
name: 'Rename Tab', name: this.translate.instant('Rename Tab'),
}, },
{ {
id: 'close-tab', id: 'close-tab',
name: 'Close tab', name: this.translate.instant('Close tab'),
}, },
{ {
id: 'reopen-tab', id: 'reopen-tab',
name: 'Reopen last tab', name: this.translate.instant('Reopen last tab'),
}, },
{ {
id: 'toggle-last-tab', id: 'toggle-last-tab',
name: 'Toggle last tab', name: this.translate.instant('Toggle last tab'),
}, },
{ {
id: 'next-tab', id: 'next-tab',
name: 'Next tab', name: this.translate.instant('Next tab'),
}, },
{ {
id: 'previous-tab', id: 'previous-tab',
name: 'Previous tab', name: this.translate.instant('Previous tab'),
}, },
{ {
id: 'move-tab-left', id: 'move-tab-left',
name: 'Move tab to the left', name: this.translate.instant('Move tab to the left'),
}, },
{ {
id: 'move-tab-right', id: 'move-tab-right',
name: 'Move tab to the right', name: this.translate.instant('Move tab to the right'),
}, },
{ {
id: 'rearrange-panes', id: 'rearrange-panes',
name: 'Show pane labels (for rearranging)', name: this.translate.instant('Show pane labels (for rearranging)'),
}, },
{ {
id: 'duplicate-tab', id: 'duplicate-tab',
name: 'Duplicate tab', name: this.translate.instant('Duplicate tab'),
}, },
{ {
id: 'tab-1', id: 'tab-1',
name: 'Tab 1', name: this.translate.instant('Tab 1'),
}, },
{ {
id: 'tab-2', id: 'tab-2',
name: 'Tab 2', name: this.translate.instant('Tab 2'),
}, },
{ {
id: 'tab-3', id: 'tab-3',
name: 'Tab 3', name: this.translate.instant('Tab 3'),
}, },
{ {
id: 'tab-4', id: 'tab-4',
name: 'Tab 4', name: this.translate.instant('Tab 4'),
}, },
{ {
id: 'tab-5', id: 'tab-5',
name: 'Tab 5', name: this.translate.instant('Tab 5'),
}, },
{ {
id: 'tab-6', id: 'tab-6',
name: 'Tab 6', name: this.translate.instant('Tab 6'),
}, },
{ {
id: 'tab-7', id: 'tab-7',
name: 'Tab 7', name: this.translate.instant('Tab 7'),
}, },
{ {
id: 'tab-8', id: 'tab-8',
name: 'Tab 8', name: this.translate.instant('Tab 8'),
}, },
{ {
id: 'tab-9', id: 'tab-9',
name: 'Tab 9', name: this.translate.instant('Tab 9'),
}, },
{ {
id: 'tab-10', id: 'tab-10',
name: 'Tab 10', name: this.translate.instant('Tab 10'),
}, },
{ {
id: 'tab-11', id: 'tab-11',
name: 'Tab 11', name: this.translate.instant('Tab 11'),
}, },
{ {
id: 'tab-12', id: 'tab-12',
name: 'Tab 12', name: this.translate.instant('Tab 12'),
}, },
{ {
id: 'tab-13', id: 'tab-13',
name: 'Tab 13', name: this.translate.instant('Tab 13'),
}, },
{ {
id: 'tab-14', id: 'tab-14',
name: 'Tab 14', name: this.translate.instant('Tab 14'),
}, },
{ {
id: 'tab-15', id: 'tab-15',
name: 'Tab 15', name: this.translate.instant('Tab 15'),
}, },
{ {
id: 'tab-16', id: 'tab-16',
name: 'Tab 16', name: this.translate.instant('Tab 16'),
}, },
{ {
id: 'tab-17', id: 'tab-17',
name: 'Tab 17', name: this.translate.instant('Tab 17'),
}, },
{ {
id: 'tab-18', id: 'tab-18',
name: 'Tab 18', name: this.translate.instant('Tab 18'),
}, },
{ {
id: 'tab-19', id: 'tab-19',
name: 'Tab 19', name: this.translate.instant('Tab 19'),
}, },
{ {
id: 'tab-20', id: 'tab-20',
name: 'Tab 20', name: this.translate.instant('Tab 20'),
}, },
{ {
id: 'split-right', id: 'split-right',
name: 'Split to the right', name: this.translate.instant('Split to the right'),
}, },
{ {
id: 'split-bottom', id: 'split-bottom',
name: 'Split to the bottom', name: this.translate.instant('Split to the bottom'),
}, },
{ {
id: 'split-left', id: 'split-left',
name: 'Split to the left', name: this.translate.instant('Split to the left'),
}, },
{ {
id: 'split-top', id: 'split-top',
name: 'Split to the top', name: this.translate.instant('Split to the top'),
}, },
{ {
id: 'pane-maximize', id: 'pane-maximize',
name: 'Maximize the active pane', name: this.translate.instant('Maximize the active pane'),
}, },
{ {
id: 'pane-nav-up', id: 'pane-nav-up',
name: 'Focus the pane above', name: this.translate.instant('Focus the pane above'),
}, },
{ {
id: 'pane-nav-down', id: 'pane-nav-down',
name: 'Focus the pane below', name: this.translate.instant('Focus the pane below'),
}, },
{ {
id: 'pane-nav-left', id: 'pane-nav-left',
name: 'Focus the pane on the left', name: this.translate.instant('Focus the pane on the left'),
}, },
{ {
id: 'pane-nav-right', id: 'pane-nav-right',
name: 'Focus the pane on the right', name: this.translate.instant('Focus the pane on the right'),
}, },
{ {
id: 'pane-nav-previous', id: 'pane-nav-previous',
name: 'Focus previous pane', name: this.translate.instant('Focus previous pane'),
}, },
{ {
id: 'pane-nav-next', id: 'pane-nav-next',
name: 'Focus next pane', name: this.translate.instant('Focus next pane'),
}, },
{ {
id: 'switch-profile', id: 'switch-profile',
name: 'Switch profile in the active pane', name: this.translate.instant('Switch profile in the active pane'),
}, },
{ {
id: 'close-pane', id: 'close-pane',
name: 'Close focused pane', name: this.translate.instant('Close focused pane'),
}, },
] ]
constructor ( constructor (
private profilesService: ProfilesService, private profilesService: ProfilesService,
private translate: TranslateService,
) { super() } ) { super() }
async provide (): Promise<HotkeyDescription[]> { async provide (): Promise<HotkeyDescription[]> {

View File

@ -1,4 +1,4 @@
import { NgModule, ModuleWithProviders } from '@angular/core' import { NgModule, ModuleWithProviders, LOCALE_ID } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser' import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
@ -7,6 +7,8 @@ import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-sc
import { NgxFilesizeModule } from 'ngx-filesize' import { NgxFilesizeModule } from 'ngx-filesize'
import { SortablejsModule } from 'ngx-sortablejs' import { SortablejsModule } from 'ngx-sortablejs'
import { DragDropModule } from '@angular/cdk/drag-drop' import { DragDropModule } from '@angular/cdk/drag-drop'
import { TranslateModule, TranslateCompiler, TranslateService } from '@ngx-translate/core'
import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler'
import { AppRootComponent } from './components/appRoot.component' import { AppRootComponent } from './components/appRoot.component'
import { CheckboxComponent } from './components/checkbox.component' import { CheckboxComponent } from './components/checkbox.component'
@ -40,6 +42,7 @@ import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service' import { ConfigService } from './services/config.service'
import { VaultFileProvider } from './services/vault.service' import { VaultFileProvider } from './services/vault.service'
import { HotkeysService } from './services/hotkeys.service' import { HotkeysService } from './services/hotkeys.service'
import { LocaleService, TranslateServiceWrapper } from './services/locale.service'
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme' import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
import { CoreConfigProvider } from './config' import { CoreConfigProvider } from './config'
@ -51,6 +54,10 @@ import { SplitLayoutProfilesService } from './profiles'
import 'perfect-scrollbar/css/perfect-scrollbar.css' import 'perfect-scrollbar/css/perfect-scrollbar.css'
export function TranslateMessageFormatCompilerFactory (): TranslateMessageFormatCompiler {
return new TranslateMessageFormatCompiler()
}
const PROVIDERS = [ const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true }, { provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true }, { provide: Theme, useClass: StandardTheme, multi: true },
@ -68,6 +75,19 @@ const PROVIDERS = [
{ provide: FileProvider, useClass: VaultFileProvider, multi: true }, { provide: FileProvider, useClass: VaultFileProvider, multi: true },
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
{ provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true }, { provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
{
provide: LOCALE_ID,
deps: [LocaleService],
useFactory: locale => locale.getLocale(),
},
{
provide: MESSAGE_FORMAT_CONFIG,
useValue: LocaleService.allLocales,
},
{
provide: TranslateService,
useClass: TranslateServiceWrapper,
},
] ]
/** @hidden */ /** @hidden */
@ -81,6 +101,7 @@ const PROVIDERS = [
PerfectScrollbarModule, PerfectScrollbarModule,
DragDropModule, DragDropModule,
SortablejsModule.forRoot({ animation: 150 }), SortablejsModule.forRoot({ animation: 150 }),
TranslateModule,
], ],
declarations: [ declarations: [
AppRootComponent, AppRootComponent,
@ -127,6 +148,7 @@ const PROVIDERS = [
AlwaysVisibleTypeaheadDirective, AlwaysVisibleTypeaheadDirective,
SortablejsModule, SortablejsModule,
DragDropModule, DragDropModule,
TranslateModule,
], ],
}) })
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
@ -135,6 +157,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
config: ConfigService, config: ConfigService,
platform: PlatformService, platform: PlatformService,
hotkeys: HotkeysService, hotkeys: HotkeysService,
public locale: LocaleService,
private translate: TranslateService,
private profilesService: ProfilesService, private profilesService: ProfilesService,
private selector: SelectorService, private selector: SelectorService,
) { ) {
@ -182,8 +206,8 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
if (provider.supportsQuickConnect) { if (provider.supportsQuickConnect) {
options.push({ options.push({
name: 'Quick connect', name: this.translate.instant('Quick connect'),
freeInputPattern: 'Connect to "%s"...', freeInputPattern: this.translate.instant('Connect to "%s"...'),
icon: 'fas fa-arrow-right', icon: 'fas fa-arrow-right',
callback: query => { callback: query => {
const p = provider.quickConnect(query) const p = provider.quickConnect(query)
@ -194,13 +218,23 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
}) })
} }
await this.selector.show('Select profile', options) await this.selector.show(this.translate.instant('Select profile'), options)
} }
static forRoot (): ModuleWithProviders<AppModule> { static forRoot (): ModuleWithProviders<AppModule> {
const translateModule = TranslateModule.forRoot({
defaultLanguage: 'en',
compiler: {
provide: TranslateCompiler,
useFactory: TranslateMessageFormatCompilerFactory,
},
})
return { return {
ngModule: AppModule, ngModule: AppModule,
providers: PROVIDERS, providers: [
...PROVIDERS,
...translateModule.providers!.filter(x => x !== TranslateService),
],
} }
} }
} }

View File

@ -1,6 +1,7 @@
import slugify from 'slugify' import slugify from 'slugify'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ConfigService, NewTabParameters, PartialProfile, Profile, ProfileProvider } from './api' import { ConfigService, NewTabParameters, PartialProfile, Profile, ProfileProvider } from './api'
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component' import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
@ -15,7 +16,7 @@ export interface SplitLayoutProfile extends Profile {
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfile> { export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfile> {
id = 'split-layout' id = 'split-layout'
name = 'Saved layout' name = this.translate.instant('Saved layout')
configDefaults = { configDefaults = {
options: { options: {
recoveryToken: null, recoveryToken: null,
@ -25,6 +26,7 @@ export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfi
constructor ( constructor (
private splitTabRecoveryProvider: SplitTabRecoveryProvider, private splitTabRecoveryProvider: SplitTabRecoveryProvider,
private config: ConfigService, private config: ConfigService,
private translate: TranslateService,
) { ) {
super() super()
} }

View File

@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid'
import * as yaml from 'js-yaml' import * as yaml from 'js-yaml'
import { Observable, Subject, AsyncSubject } from 'rxjs' import { Observable, Subject, AsyncSubject } from 'rxjs'
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { ConfigProvider } from '../api/configProvider' import { ConfigProvider } from '../api/configProvider'
import { PlatformService } from '../api/platform' import { PlatformService } from '../api/platform'
import { HostAppService } from '../api/hostApp' import { HostAppService } from '../api/hostApp'
@ -136,6 +137,7 @@ export class ConfigService {
private hostApp: HostAppService, private hostApp: HostAppService,
private platform: PlatformService, private platform: PlatformService,
private vault: VaultService, private vault: VaultService,
private translate: TranslateService,
@Inject(ConfigProvider) private configProviders: ConfigProvider[], @Inject(ConfigProvider) private configProviders: ConfigProvider[],
) { ) {
this.defaults = this.mergeDefaults() this.defaults = this.mergeDefaults()
@ -360,9 +362,13 @@ export class ConfigService {
} catch (e) { } catch (e) {
let result = await this.platform.showMessageBox({ let result = await this.platform.showMessageBox({
type: 'error', type: 'error',
message: 'Could not decrypt config', message: this.translate.instant('Could not decrypt config'),
detail: e.toString(), detail: e.toString(),
buttons: ['Try again', 'Erase config', 'Quit'], buttons: [
this.translate.instant('Try again'),
this.translate.instant('Erase config'),
this.translate.instant('Quit'),
],
defaultId: 0, defaultId: 0,
}) })
if (result.response === 2) { if (result.response === 2) {
@ -371,9 +377,12 @@ export class ConfigService {
if (result.response === 1) { if (result.response === 1) {
result = await this.platform.showMessageBox({ result = await this.platform.showMessageBox({
type: 'warning', type: 'warning',
message: 'Are you sure?', message: this.translate.instant('Are you sure?'),
detail: e.toString(), detail: e.toString(),
buttons: ['Erase config', 'Quit'], buttons: [
this.translate.instant('Erase config'),
this.translate.instant('Quit'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
}) })

View File

@ -1,4 +1,5 @@
import { Inject, Injectable } from '@angular/core' import { Inject, Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { FileProvider, NotificationsService, SelectorService } from '../api' import { FileProvider, NotificationsService, SelectorService } from '../api'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@ -7,6 +8,7 @@ export class FileProvidersService {
private constructor ( private constructor (
private selector: SelectorService, private selector: SelectorService,
private notifications: NotificationsService, private notifications: NotificationsService,
private translate: TranslateService,
@Inject(FileProvider) private fileProviders: FileProvider[], @Inject(FileProvider) private fileProviders: FileProvider[],
) { } ) { }
@ -34,15 +36,18 @@ export class FileProvidersService {
} }
})) }))
if (!providers.length) { if (!providers.length) {
this.notifications.error('Vault master passphrase needs to be set to allow storing secrets') this.notifications.error(this.translate.instant('Vault master passphrase needs to be set to allow storing secrets'))
throw new Error('No available file providers') throw new Error('No available file providers')
} }
if (providers.length === 1) { if (providers.length === 1) {
return providers[0] return providers[0]
} }
return this.selector.show('Select file storage', providers.map(p => ({ return this.selector.show(
name: p.name, this.translate.instant('Select file storage'),
result: p, providers.map(p => ({
}))) name: p.name,
result: p,
}))
)
} }
} }

View File

@ -0,0 +1,134 @@
import { Injectable } from '@angular/core'
import { registerLocaleData } from '@angular/common'
import { TranslateService } from '@ngx-translate/core'
import localeEN from '@angular/common/locales/en-GB'
import localeRU from '@angular/common/locales/ru'
import { Observable, Subject } from 'rxjs'
import { distinctUntilChanged } from 'rxjs/operators'
import { ConfigService } from './config.service'
import { LogService, Logger } from './log.service'
registerLocaleData(localeEN)
registerLocaleData(localeRU)
@Injectable({ providedIn: 'root' })
export class TranslateServiceWrapper extends TranslateService {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
getParsedResult (translations: any, key: any, interpolateParams?: any): any {
this.translations[this.defaultLang][key] ??= this.compiler.compile(key, this.defaultLang)
return super.getParsedResult(translations, key, interpolateParams)
}
}
@Injectable({ providedIn: 'root' })
export class LocaleService {
private logger: Logger
static readonly allLocales = ['en', 'de', 'fr', 'ru']
get localeChanged$ (): Observable<string> {
return this.localeChanged.pipe(distinctUntilChanged())
}
get catalogChanged$ (): Observable<Record<string, string | undefined>> {
return this.catalogChanged.pipe(distinctUntilChanged())
}
readonly allLanguages: { code: string, name: string }[]
private translations = {
en: {
Close: 'Close',
},
ru: {
Close: 'Закрыть',
},
}
private locale = 'en'
private localeChanged = new Subject<string>()
private catalogChanged = new Subject<Record<string, string | undefined>>()
constructor (
private config: ConfigService,
private translate: TranslateService,
log: LogService,
) {
this.logger = log.create('translate')
config.changed$.subscribe(() => {
this.refresh()
})
config.ready$.subscribe(() => {
this.refresh()
})
this.allLanguages = [
{
code: 'en',
name: translate.instant('English'),
},
{
code: 'de',
name: translate.instant('German'),
},
{
code: 'fr',
name: translate.instant('French'),
},
/* {
code: 'it',
name: translate.instant('Italian'),
},
{
code: 'es',
name: translate.instant('Spanish'),
}, */
{
code: 'ru',
name: translate.instant('Russian'),
},
/* {
code: 'ar',
name: translate.instant('Arabic'),
}, */
]
}
refresh (): void {
let lang = this.config.store.language
if (!lang) {
const systemLanguage = navigator.language.toLowerCase().split('-')[0]
if (this.allLanguages.some(x => x.code === systemLanguage)) {
lang = systemLanguage
}
}
lang ??= 'en'
this.setLocale(lang)
}
async setLocale (lang: string): Promise<void> {
const strings = this.translations[lang]
if (!this.translate.langs.includes(lang)) {
this.translate.addLangs([lang])
const po = require(`../../../locale/${lang}.po`).translations['']
const translation = {}
for (const k of Object.keys(po)) {
translation[k] = po[k].msgstr[0] || k
}
this.translate.setTranslation(lang, translation)
}
this.translate.setDefaultLang(lang)
this.locale = lang
this.localeChanged.next(lang)
this.logger.debug('Setting language to', lang)
this.catalogChanged.next(strings)
}
getLocale (): string {
return this.locale
}
}

View File

@ -1,4 +1,5 @@
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { NewTabParameters } from './tabs.service' import { NewTabParameters } from './tabs.service'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { PartialProfile, Profile, ProfileProvider } from '../api/profileProvider' import { PartialProfile, Profile, ProfileProvider } from '../api/profileProvider'
@ -29,6 +30,7 @@ export class ProfilesService {
private config: ConfigService, private config: ConfigService,
private notifications: NotificationsService, private notifications: NotificationsService,
private selector: SelectorService, private selector: SelectorService,
private translate: TranslateService,
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[], @Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
) { } ) { }
@ -103,7 +105,7 @@ export class ProfilesService {
let options: SelectorOption<void>[] = recentProfiles.map(p => ({ let options: SelectorOption<void>[] = recentProfiles.map(p => ({
...this.selectorOptionForProfile(p), ...this.selectorOptionForProfile(p),
group: 'Recent', group: this.translate.instant('Recent'),
icon: 'fas fa-history', icon: 'fas fa-history',
color: p.color, color: p.color,
callback: async () => { callback: async () => {
@ -115,8 +117,8 @@ export class ProfilesService {
})) }))
if (recentProfiles.length) { if (recentProfiles.length) {
options.push({ options.push({
name: 'Clear recent profiles', name: this.translate.instant('Clear recent profiles'),
group: 'Recent', group: this.translate.instant('Recent'),
icon: 'fas fa-eraser', icon: 'fas fa-eraser',
callback: async () => { callback: async () => {
window.localStorage.removeItem('recentProfiles') window.localStorage.removeItem('recentProfiles')
@ -142,7 +144,7 @@ export class ProfilesService {
try { try {
const { SettingsTabComponent } = window['nodeRequire']('tabby-settings') const { SettingsTabComponent } = window['nodeRequire']('tabby-settings')
options.push({ options.push({
name: 'Manage profiles', name: this.translate.instant('Manage profiles'),
icon: 'fas fa-window-restore', icon: 'fas fa-window-restore',
callback: () => { callback: () => {
this.app.openNewTabRaw({ this.app.openNewTabRaw({
@ -156,8 +158,8 @@ export class ProfilesService {
if (this.getProviders().some(x => x.supportsQuickConnect)) { if (this.getProviders().some(x => x.supportsQuickConnect)) {
options.push({ options.push({
name: 'Quick connect', name: this.translate.instant('Quick connect'),
freeInputPattern: 'Connect to "%s"...', freeInputPattern: this.translate.instant('Connect to "%s"...'),
icon: 'fas fa-arrow-right', icon: 'fas fa-arrow-right',
callback: query => { callback: query => {
const profile = this.quickConnect(query) const profile = this.quickConnect(query)
@ -165,7 +167,7 @@ export class ProfilesService {
}, },
}) })
} }
await this.selector.show('Select profile or enter an address', options) await this.selector.show(this.translate.instant('Select profile or enter an address'), options)
} catch (err) { } catch (err) {
reject(err) reject(err)
} }

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { TranslateService } from '@ngx-translate/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
import { BaseTabComponent } from './components/baseTab.component' import { BaseTabComponent } from './components/baseTab.component'
@ -22,6 +23,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
constructor ( constructor (
private app: AppService, private app: AppService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -29,7 +31,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
let items: MenuItemOptions[] = [ let items: MenuItemOptions[] = [
{ {
label: 'Close', label: this.translate.instant('Close'),
click: () => { click: () => {
if (this.app.tabs.includes(tab)) { if (this.app.tabs.includes(tab)) {
this.app.closeTab(tab, true) this.app.closeTab(tab, true)
@ -43,7 +45,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
items = [ items = [
...items, ...items,
{ {
label: 'Close other tabs', label: this.translate.instant('Close other tabs'),
click: () => { click: () => {
for (const t of this.app.tabs.filter(x => x !== tab)) { for (const t of this.app.tabs.filter(x => x !== tab)) {
this.app.closeTab(t, true) this.app.closeTab(t, true)
@ -51,7 +53,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
}, },
}, },
{ {
label: 'Close tabs to the right', label: this.translate.instant('Close tabs to the right'),
click: () => { click: () => {
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) { for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
this.app.closeTab(t, true) this.app.closeTab(t, true)
@ -59,7 +61,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
}, },
}, },
{ {
label: 'Close tabs to the left', label: this.translate.instant('Close tabs to the left'),
click: () => { click: () => {
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) { for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
this.app.closeTab(t, true) this.app.closeTab(t, true)
@ -71,13 +73,13 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
if (tab.parent instanceof SplitTabComponent) { if (tab.parent instanceof SplitTabComponent) {
const directions: SplitDirection[] = ['r', 'b', 'l', 't'] const directions: SplitDirection[] = ['r', 'b', 'l', 't']
items.push({ items.push({
label: 'Split', label: this.translate.instant('Split'),
submenu: directions.map(dir => ({ submenu: directions.map(dir => ({
label: { label: {
r: 'Right', r: this.translate.instant('Right'),
b: 'Down', b: this.translate.instant('Down'),
l: 'Left', l: this.translate.instant('Left'),
t: 'Up', t: this.translate.instant('Up'),
}[dir], }[dir],
click: () => { click: () => {
(tab.parent as SplitTabComponent).splitTab(tab, dir) (tab.parent as SplitTabComponent).splitTab(tab, dir)
@ -99,6 +101,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
private app: AppService, private app: AppService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private splitLayoutProfilesService: SplitLayoutProfilesService, private splitLayoutProfilesService: SplitLayoutProfilesService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -109,18 +112,18 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
items = [ items = [
...items, ...items,
{ {
label: 'Rename', label: this.translate.instant('Rename'),
click: () => tabHeader.showRenameTabModal(), click: () => tabHeader.showRenameTabModal(),
}, },
{ {
label: 'Duplicate', label: this.translate.instant('Duplicate'),
click: () => this.app.duplicateTab(tab), click: () => this.app.duplicateTab(tab),
}, },
{ {
label: 'Color', label: this.translate.instant('Color'),
sublabel: TAB_COLORS.find(x => x.value === tab.color)?.name, sublabel: TAB_COLORS.find(x => x.value === tab.color)?.name,
submenu: TAB_COLORS.map(color => ({ submenu: TAB_COLORS.map(color => ({
label: color.name, label: this.translate.instant(color.name),
type: 'radio', type: 'radio',
checked: tab.color === color.value, checked: tab.color === color.value,
click: () => { click: () => {
@ -132,10 +135,10 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
if (tab instanceof SplitTabComponent && tab.getAllTabs().length > 1) { if (tab instanceof SplitTabComponent && tab.getAllTabs().length > 1) {
items.push({ items.push({
label: 'Save layout as profile', label: this.translate.instant('Save layout as profile'),
click: async () => { click: async () => {
const modal = this.ngbModal.open(PromptModalComponent) const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'Profile name' modal.componentInstance.prompt = this.translate.instant('Profile name')
const name = (await modal.result)?.value const name = (await modal.result)?.value
if (!name) { if (!name) {
return return
@ -154,6 +157,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
export class TaskCompletionContextMenu extends TabContextMenuItemProvider { export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
constructor ( constructor (
private app: AppService, private app: AppService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -167,10 +171,10 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (process) { if (process) {
items.push({ items.push({
enabled: false, enabled: false,
label: 'Current process: ' + process.name, label: this.translate.instant('Current process: {name}', process),
}) })
items.push({ items.push({
label: 'Notify when done', label: this.translate.instant('Notify when done'),
type: 'checkbox', type: 'checkbox',
checked: extTab.__completionNotificationEnabled, checked: extTab.__completionNotificationEnabled,
click: () => { click: () => {
@ -178,7 +182,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (extTab.__completionNotificationEnabled) { if (extTab.__completionNotificationEnabled) {
this.app.observeTabCompletion(tab).subscribe(() => { this.app.observeTabCompletion(tab).subscribe(() => {
new Notification('Process completed', { new Notification(this.translate.instant('Process completed'), {
body: process.name, body: process.name,
}).addEventListener('click', () => { }).addEventListener('click', () => {
this.app.selectTab(tab) this.app.selectTab(tab)
@ -192,7 +196,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
}) })
} }
items.push({ items.push({
label: 'Notify on activity', label: this.translate.instant('Notify on activity'),
type: 'checkbox', type: 'checkbox',
checked: !!extTab.__outputNotificationSubscription, checked: !!extTab.__outputNotificationSubscription,
click: () => { click: () => {
@ -204,7 +208,7 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
if (extTab.__outputNotificationSubscription && active) { if (extTab.__outputNotificationSubscription && active) {
extTab.__outputNotificationSubscription.unsubscribe() extTab.__outputNotificationSubscription.unsubscribe()
extTab.__outputNotificationSubscription = null extTab.__outputNotificationSubscription = null
new Notification('Tab activity', { new Notification(this.translate.instant('Tab activity'), {
body: tab.title, body: tab.title,
}).addEventListener('click', () => { }).addEventListener('click', () => {
this.app.selectTab(tab) this.app.selectTab(tab)
@ -228,6 +232,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
private profilesService: ProfilesService, private profilesService: ProfilesService,
private tabsService: TabsService, private tabsService: TabsService,
private app: AppService, private app: AppService,
private translate: TranslateService,
hotkeys: HotkeysService, hotkeys: HotkeysService,
) { ) {
super() super()
@ -270,7 +275,7 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) { if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
return [ return [
{ {
label: 'Switch profile', label: this.translate.instant('Switch profile'),
click: () => this.switchTabProfile(tab), click: () => this.switchTabProfile(tab),
}, },
] ]

View File

@ -1,28 +1,41 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TranslateService } from '@ngx-translate/core'
import { Theme } from './api' import { Theme } from './api'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class StandardTheme extends Theme { export class StandardTheme extends Theme {
name = 'Standard' name = this.translate.instant('Standard')
css = require('./theme.scss') css = require('./theme.scss')
terminalBackground = '#222a33' terminalBackground = '#222a33'
constructor (private translate: TranslateService) {
super()
}
} }
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class StandardCompactTheme extends Theme { export class StandardCompactTheme extends Theme {
name = 'Compact' name = this.translate.instant('Compact')
css = require('./theme.compact.scss') css = require('./theme.compact.scss')
terminalBackground = '#222a33' terminalBackground = '#222a33'
macOSWindowButtonsInsetX = 8 macOSWindowButtonsInsetX = 8
macOSWindowButtonsInsetY = 6 macOSWindowButtonsInsetY = 6
constructor (private translate: TranslateService) {
super()
}
} }
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class PaperTheme extends Theme { export class PaperTheme extends Theme {
name = 'Paper' name = this.translate.instant('Paper')
css = require('./theme.paper.scss') css = require('./theme.paper.scss')
terminalBackground = '#f7f1e0' terminalBackground = '#f7f1e0'
constructor (private translate: TranslateService) {
super()
}
} }

View File

@ -1,5 +1,6 @@
import * as os from 'os' import * as os from 'os'
import { NgZone } from '@angular/core' import { NgZone } from '@angular/core'
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
export const WIN_BUILD_CONPTY_SUPPORTED = 17692 export const WIN_BUILD_CONPTY_SUPPORTED = 17692
export const WIN_BUILD_CONPTY_STABLE = 18309 export const WIN_BUILD_CONPTY_STABLE = 18309
@ -56,13 +57,13 @@ export class ResettableTimeout {
} }
export const TAB_COLORS = [ export const TAB_COLORS = [
{ name: 'No color', value: null }, { name: _('No color'), value: null },
{ name: 'Blue', value: '#0275d8' }, { name: _('Blue'), value: '#0275d8' },
{ name: 'Green', value: '#5cb85c' }, { name: _('Green'), value: '#5cb85c' },
{ name: 'Orange', value: '#f0ad4e' }, { name: _('Orange'), value: '#f0ad4e' },
{ name: 'Purple', value: '#613d7c' }, { name: _('Purple'), value: '#613d7c' },
{ name: 'Red', value: '#d9534f' }, { name: _('Red'), value: '#d9534f' },
{ name: 'Yellow', value: '#ffd500' }, { name: _('Yellow'), value: '#ffd500' },
] ]
export function serializeFunction <T extends () => Promise<any>> (fn: T): T { export function serializeFunction <T extends () => Promise<any>> (fn: T): T {

View File

@ -2,6 +2,13 @@
# yarn lockfile v1 # yarn lockfile v1
"@ngx-translate/core@^14.0.0":
version "14.0.0"
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
integrity sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==
dependencies:
tslib "^2.3.0"
agent-base@6: agent-base@6:
version "6.0.2" version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@ -56,6 +63,37 @@ js-yaml@^4.0.0:
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
make-plural@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-4.3.0.tgz#f23de08efdb0cac2e0c9ba9f315b0dff6b4c2735"
integrity sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==
optionalDependencies:
minimist "^1.2.0"
messageformat-formatters@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz#0492c1402a48775f751c9b17c0354e92be012b08"
integrity sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==
messageformat-parser@^4.1.2:
version "4.1.3"
resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-4.1.3.tgz#b824787f57fcda7d50769f5b63e8d4fda68f5b9e"
integrity sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==
messageformat@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-2.3.0.tgz#de263c49029d5eae65d7ee25e0754f57f425ad91"
integrity sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==
dependencies:
make-plural "^4.3.0"
messageformat-formatters "^2.0.1"
messageformat-parser "^4.1.2"
minimist@^1.2.0:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mixpanel@^0.13.0: mixpanel@^0.13.0:
version "0.13.0" version "0.13.0"
resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.13.0.tgz#699bf510d9ba013c75edcf979ff1e24085fde9d2" resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.13.0.tgz#699bf510d9ba013c75edcf979ff1e24085fde9d2"
@ -85,6 +123,13 @@ ngx-perfect-scrollbar@^10.1.0:
resize-observer-polyfill "^1.5.0" resize-observer-polyfill "^1.5.0"
tslib "^2.0.0" tslib "^2.0.0"
ngx-translate-messageformat-compiler@^4.11.0:
version "4.11.0"
resolved "https://registry.yarnpkg.com/ngx-translate-messageformat-compiler/-/ngx-translate-messageformat-compiler-4.11.0.tgz#c9b71dd139ba5fcdcd809001e22622de589fd707"
integrity sha512-OdGfWV4fF3DhZqGIHcLmOnQDufugmZ+E90NYr1UPGRZgT10lilr9oLmIrisy3lW4THnZFNo9JXsX7+fX84LbDw==
dependencies:
tslib "^1.10.0"
perfect-scrollbar@1.5.0: perfect-scrollbar@1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83" resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83"
@ -116,11 +161,21 @@ string_decoder@^1.1.1:
dependencies: dependencies:
safe-buffer "~5.2.0" safe-buffer "~5.2.0"
tslib@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0: tslib@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tslib@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
util-deprecate@^1.0.1: util-deprecate@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core' import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
@ -7,14 +7,16 @@ export class ElectronHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [ hotkeys: HotkeyDescription[] = [
{ {
id: 'new-window', id: 'new-window',
name: 'New window', name: this.translate.instant('New window'),
}, },
{ {
id: 'toggle-window', id: 'toggle-window',
name: 'Toggle terminal window', name: this.translate.instant('Toggle terminal window'),
}, },
] ]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> { async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys return this.hotkeys
} }

View File

@ -7,7 +7,7 @@ import { promisify } from 'util'
import promiseIpc, { RendererProcessType } from 'electron-promise-ipc' import promiseIpc, { RendererProcessType } from 'electron-promise-ipc'
import { execFile } from 'mz/child_process' import { execFile } from 'mz/child_process'
import { Injectable, NgZone } from '@angular/core' import { Injectable, NgZone } from '@angular/core'
import { PlatformService, ClipboardContent, HostAppService, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise } from 'tabby-core' import { PlatformService, ClipboardContent, HostAppService, Platform, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileDownload, FileUploadOptions, wrapPromise, TranslateService } from 'tabby-core'
import { ElectronService } from '../services/electron.service' import { ElectronService } from '../services/electron.service'
import { ElectronHostWindow } from './hostWindow.service' import { ElectronHostWindow } from './hostWindow.service'
import { ShellIntegrationService } from './shellIntegration.service' import { ShellIntegrationService } from './shellIntegration.service'
@ -34,6 +34,7 @@ export class ElectronPlatformService extends PlatformService {
private electron: ElectronService, private electron: ElectronService,
private zone: NgZone, private zone: NgZone,
private shellIntegration: ShellIntegrationService, private shellIntegration: ShellIntegrationService,
private translate: TranslateService,
) { ) {
super() super()
this.configPath = path.join(electron.app.getPath('userData'), 'config.yaml') this.configPath = path.join(electron.app.getPath('userData'), 'config.yaml')
@ -204,7 +205,7 @@ export class ElectronPlatformService extends PlatformService {
const result = await this.electron.dialog.showOpenDialog( const result = await this.electron.dialog.showOpenDialog(
this.hostWindow.getWindow(), this.hostWindow.getWindow(),
{ {
buttonLabel: 'Select', buttonLabel: this.translate.instant('Select'),
properties, properties,
}, },
) )

View File

@ -2,7 +2,7 @@ import type { AppUpdater } from 'electron-updater'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import axios from 'axios' import axios from 'axios'
import { Logger, LogService, ConfigService, UpdaterService, PlatformService } from 'tabby-core' import { Logger, LogService, ConfigService, UpdaterService, PlatformService, TranslateService } from 'tabby-core'
import { ElectronService } from '../services/electron.service' import { ElectronService } from '../services/electron.service'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/tabby/releases/latest' const UPDATES_URL = 'https://api.github.com/repos/eugeny/tabby/releases/latest'
@ -18,6 +18,7 @@ export class ElectronUpdaterService extends UpdaterService {
constructor ( constructor (
log: LogService, log: LogService,
config: ConfigService, config: ConfigService,
private translate: TranslateService,
private platform: PlatformService, private platform: PlatformService,
private electron: ElectronService, private electron: ElectronService,
) { ) {
@ -132,8 +133,11 @@ export class ElectronUpdaterService extends UpdaterService {
if ((await this.platform.showMessageBox( if ((await this.platform.showMessageBox(
{ {
type: 'warning', type: 'warning',
message: 'Installing the update will close all tabs and restart Tabby.', message: this.translate.instant('Installing the update will close all tabs and restart Tabby.'),
buttons: ['Update', 'Cancel'], buttons: [
this.translate.instant('Update'),
this.translate.instant('Cancel'),
],
defaultId: 0, defaultId: 0,
cancelId: 1, cancelId: 1,
} }

View File

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@angular/core' import { Inject, Injectable } from '@angular/core'
import { ConfigService, PlatformService } from 'tabby-core' import { ConfigService, PlatformService, TranslateService } from 'tabby-core'
import { TerminalDecorator, BaseTerminalTabComponent } from 'tabby-terminal' import { TerminalDecorator, BaseTerminalTabComponent } from 'tabby-terminal'
import { LinkHandler } from './api' import { LinkHandler } from './api'
@ -9,6 +9,7 @@ export class LinkHighlighterDecorator extends TerminalDecorator {
constructor ( constructor (
private config: ConfigService, private config: ConfigService,
private platform: PlatformService, private platform: PlatformService,
private translate: TranslateService,
@Inject(LinkHandler) private handlers: LinkHandler[], @Inject(LinkHandler) private handlers: LinkHandler[],
) { ) {
super() super()
@ -42,13 +43,13 @@ export class LinkHighlighterDecorator extends TerminalDecorator {
this.platform.popupContextMenu([ this.platform.popupContextMenu([
{ {
click: () => openLink(uri), click: () => openLink(uri),
label: 'Open', label: this.translate.instant('Open'),
}, },
{ {
click: async () => { click: async () => {
this.platform.setClipboard({ text: await getLink(uri) }) this.platform.setClipboard({ text: await getLink(uri) })
}, },
label: 'Copy', label: this.translate.instant('Copy'),
}, },
]) ])
return false return false

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton } from 'tabby-core' import { ToolbarButtonProvider, ToolbarButton, TranslateService } from 'tabby-core'
import { TerminalService } from './services/terminal.service' import { TerminalService } from './services/terminal.service'
/** @hidden */ /** @hidden */
@ -8,6 +8,7 @@ import { TerminalService } from './services/terminal.service'
export class ButtonProvider extends ToolbarButtonProvider { export class ButtonProvider extends ToolbarButtonProvider {
constructor ( constructor (
private terminal: TerminalService, private terminal: TerminalService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -16,7 +17,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
return [ return [
{ {
icon: require('./icons/plus.svg'), icon: require('./icons/plus.svg'),
title: 'New terminal', title: this.translate.instant('New terminal'),
touchBarNSImage: 'NSTouchBarAddDetailTemplate', touchBarNSImage: 'NSTouchBarAddDetailTemplate',
click: () => { click: () => {
this.terminal.openTab() this.terminal.openTab()

View File

@ -1,6 +1,6 @@
ng-container(*ngIf='!argvMode') ng-container(*ngIf='!argvMode')
.form-group .form-group
label Command line label(translate) Command line
.input-group .input-group
.input-group-prepend .input-group-prepend
a.input-group-text( a.input-group-text(
@ -16,7 +16,7 @@ ng-container(*ngIf='!argvMode')
ng-container(*ngIf='argvMode') ng-container(*ngIf='argvMode')
.form-group .form-group
label Program label(translate) Program
.input-group .input-group
.input-group-prepend .input-group-prepend
a.input-group-text( a.input-group-text(
@ -31,7 +31,7 @@ ng-container(*ngIf='argvMode')
) )
.form-group .form-group
label Arguments label(translate) Arguments
.input-group( .input-group(
*ngFor='let arg of _model.args; index as i; trackBy: trackByIndex', *ngFor='let arg of _model.args; index as i; trackBy: trackByIndex',
) )
@ -46,4 +46,4 @@ ng-container(*ngIf='argvMode')
.mt-2 .mt-2
button.btn.btn-secondary((click)='_model.args.push("")') button.btn.btn-secondary((click)='_model.args.push("")')
i.fas.fa-plus.mr-2 i.fas.fa-plus.mr-2
| Add span(translate) Add

View File

@ -11,10 +11,10 @@
.d-flex .d-flex
button.btn.btn-secondary((click)='addEnvironmentVar()') button.btn.btn-secondary((click)='addEnvironmentVar()')
i.fas.fa-plus.mr-2 i.fas.fa-plus.mr-2
span Add span(translate) Add
.ml-auto .ml-auto
.text-muted Substitutions allowed. .text-muted(translate) Substitutions allowed.
.d-flex.ml-1(*ngIf='shouldShowExample()') .d-flex.ml-1(*ngIf='shouldShowExample()')
.text-muted Example: .text-muted(translate) Example:
a.ml-1((click)='addExample()', href='#') extend PATH a.ml-1((click)='addExample()', href='#') extend PATH

View File

@ -2,13 +2,13 @@ command-line-editor([model]='profile.options')
.form-line(*ngIf='uac.isAvailable') .form-line(*ngIf='uac.isAvailable')
.header .header
.title Run as administrator .title(translate) Run as administrator
toggle( toggle(
[(ngModel)]='profile.options.runAsAdministrator', [(ngModel)]='profile.options.runAsAdministrator',
) )
.form-group .form-group
label Working directory label(translate) Working directory
.input-group .input-group
input.form-control( input.form-control(
@ -21,7 +21,7 @@ command-line-editor([model]='profile.options')
i.fas.fa-folder-open i.fas.fa-folder-open
.form-group .form-group
label Environment label(translate) Environment
environment-editor( environment-editor(
type='text', type='text',
[(model)]='profile.options.env', [(model)]='profile.options.env',

View File

@ -1,9 +1,9 @@
h3.mb-3 Shell h3.mb-3(translate) Shell
.form-line(*ngIf='isConPTYAvailable') .form-line(*ngIf='isConPTYAvailable')
.header .header
.title Use ConPTY .title(translate) Use ConPTY
.description Enables the experimental Windows ConPTY API .description(translate) Enables the experimental Windows ConPTY API
toggle( toggle(
[(ngModel)]='config.store.terminal.useConPTY', [(ngModel)]='config.store.terminal.useConPTY',
@ -11,7 +11,7 @@ h3.mb-3 Shell
) )
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.useConPTY && isConPTYAvailable && !isConPTYStable') .alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.useConPTY && isConPTYAvailable && !isConPTYStable')
.mr-auto Windows 10 build 18309 or above is recommended for ConPTY .mr-auto(translate) Windows 10 build 18309 or above is recommended for ConPTY
.alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.profile.startsWith("WSL") && (!config.store.terminal.useConPTY)') .alert.alert-info.d-flex.align-items-center(*ngIf='config.store.terminal.profile.startsWith("WSL") && (!config.store.terminal.useConPTY)')
.mr-auto WSL terminal only supports TrueColor with ConPTY .mr-auto(translate) WSL terminal only supports TrueColor with ConPTY

View File

@ -1,5 +1,5 @@
import { Component, Input, Injector } from '@angular/core' import { Component, Input, Injector } from '@angular/core'
import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'tabby-core' import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild, TranslateService } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal' import { BaseTerminalTabComponent } from 'tabby-terminal'
import { LocalProfile, SessionOptions } from '../api' import { LocalProfile, SessionOptions } from '../api'
import { Session } from '../session' import { Session } from '../session'
@ -20,6 +20,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor ( constructor (
injector: Injector, injector: Injector,
private translate: TranslateService,
private uac: UACService, private uac: UACService,
) { ) {
super(injector) super(injector)
@ -108,8 +109,14 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
return (await this.platform.showMessageBox( return (await this.platform.showMessageBox(
{ {
type: 'warning', type: 'warning',
message: `"${children[0].command}" is still running. Close?`, message: this.translate.instant(
buttons: ['Kill', 'Cancel'], '"{command}" is still running. Close?',
children[0],
),
buttons: [
this.translate.instant('Kill'),
this.translate.instant('Cancel'),
],
defaultId: 0, defaultId: 0,
cancelId: 1, cancelId: 1,
} }

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core' import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
@ -7,10 +7,12 @@ export class LocalTerminalHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [ hotkeys: HotkeyDescription[] = [
{ {
id: 'new-tab', id: 'new-tab',
name: 'New tab', name: this.translate.instant('New tab'),
}, },
] ]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> { async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys return this.hotkeys
} }

View File

@ -1,6 +1,6 @@
import deepClone from 'clone-deep' import deepClone from 'clone-deep'
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { ProfileProvider, NewTabParameters, ConfigService, SplitTabComponent, AppService, PartialProfile } from 'tabby-core' import { ProfileProvider, NewTabParameters, ConfigService, SplitTabComponent, AppService, PartialProfile, TranslateService } from 'tabby-core'
import { TerminalTabComponent } from './components/terminalTab.component' import { TerminalTabComponent } from './components/terminalTab.component'
import { LocalProfileSettingsComponent } from './components/localProfileSettings.component' import { LocalProfileSettingsComponent } from './components/localProfileSettings.component'
import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api' import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api'
@ -8,7 +8,7 @@ import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class LocalProfilesService extends ProfileProvider<LocalProfile> { export class LocalProfilesService extends ProfileProvider<LocalProfile> {
id = 'local' id = 'local'
name = 'Local' name = this.translate.instant('Local terminal')
settingsComponent = LocalProfileSettingsComponent settingsComponent = LocalProfileSettingsComponent
configDefaults = { configDefaults = {
options: { options: {
@ -29,6 +29,7 @@ export class LocalProfilesService extends ProfileProvider<LocalProfile> {
constructor ( constructor (
private app: AppService, private app: AppService,
private config: ConfigService, private config: ConfigService,
private translate: TranslateService,
@Inject(ShellProvider) private shellProviders: ShellProvider[], @Inject(ShellProvider) private shellProviders: ShellProvider[],
) { ) {
super() super()

View File

@ -1,5 +1,5 @@
import { NgZone, Injectable } from '@angular/core' import { NgZone, Injectable } from '@angular/core'
import { ConfigService, HostAppService, Platform, ProfilesService } from 'tabby-core' import { ConfigService, HostAppService, Platform, ProfilesService, TranslateService } from 'tabby-core'
import { ElectronService } from 'tabby-electron' import { ElectronService } from 'tabby-electron'
/** @hidden */ /** @hidden */
@ -13,6 +13,7 @@ export class DockMenuService {
private hostApp: HostAppService, private hostApp: HostAppService,
private zone: NgZone, private zone: NgZone,
private profilesService: ProfilesService, private profilesService: ProfilesService,
private translate: TranslateService,
) { ) {
config.changed$.subscribe(() => this.update()) config.changed$.subscribe(() => this.update())
} }
@ -21,7 +22,7 @@ export class DockMenuService {
if (this.hostApp.platform === Platform.Windows) { if (this.hostApp.platform === Platform.Windows) {
this.electron.app.setJumpList(this.config.store.profiles.length ? [{ this.electron.app.setJumpList(this.config.store.profiles.length ? [{
type: 'custom', type: 'custom',
name: 'Profiles', name: this.translate.instant('Profiles'),
items: this.config.store.profiles.map(profile => ({ items: this.config.store.profiles.map(profile => ({
type: 'task', type: 'task',
program: process.execPath, program: process.execPath,

View File

@ -1,6 +1,6 @@
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HostAppService, Platform, LogService, Logger } from 'tabby-core' import { HostAppService, Platform, LogService, Logger, TranslateService } from 'tabby-core'
import { ShellProvider, Shell } from '../api' import { ShellProvider, Shell } from '../api'
@ -11,6 +11,7 @@ export class LinuxDefaultShellProvider extends ShellProvider {
constructor ( constructor (
private hostApp: HostAppService, private hostApp: HostAppService,
private translate: TranslateService,
log: LogService, log: LogService,
) { ) {
super() super()
@ -27,14 +28,14 @@ export class LinuxDefaultShellProvider extends ShellProvider {
this.logger.warn('Could not detect user shell') this.logger.warn('Could not detect user shell')
return [{ return [{
id: 'default', id: 'default',
name: 'User default', name: this.translate.instant('User default'),
command: '/bin/sh', command: '/bin/sh',
env: {}, env: {},
}] }]
} else { } else {
return [{ return [{
id: 'default', id: 'default',
name: 'User default', name: this.translate.instant('User default'),
command: line.split(':')[6], command: line.split(':')[6],
args: ['--login'], args: ['--login'],
hidden: true, hidden: true,

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import promiseIpc, { RendererProcessType } from 'electron-promise-ipc' import promiseIpc, { RendererProcessType } from 'electron-promise-ipc'
import { HostAppService, Platform } from 'tabby-core' import { HostAppService, Platform, TranslateService } from 'tabby-core'
import { ShellProvider, Shell } from '../api' import { ShellProvider, Shell } from '../api'
@ -11,6 +11,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
constructor ( constructor (
private hostApp: HostAppService, private hostApp: HostAppService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -21,7 +22,7 @@ export class MacOSDefaultShellProvider extends ShellProvider {
} }
return [{ return [{
id: 'default', id: 'default',
name: 'OS default', name: this.translate.instant('OS default'),
command: await this.getDefaultShellCached(), command: await this.getDefaultShellCached(),
args: ['--login'], args: ['--login'],
hidden: true, hidden: true,

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'tabby-core' import { HostAppService, Platform, TranslateService } from 'tabby-core'
import { ShellProvider, Shell } from '../api' import { ShellProvider, Shell } from '../api'
@ -17,6 +17,7 @@ export class WindowsDefaultShellProvider extends ShellProvider {
wsl: WSLShellProvider, wsl: WSLShellProvider,
stock: WindowsStockShellsProvider, stock: WindowsStockShellsProvider,
private hostApp: HostAppService, private hostApp: HostAppService,
private translate: TranslateService,
) { ) {
super() super()
this.providers = [ this.providers = [
@ -39,7 +40,7 @@ export class WindowsDefaultShellProvider extends ShellProvider {
return [{ return [{
...shell, ...shell,
id: 'default', id: 'default',
name: `OS default (${shell.name})`, name: this.translate.instant('OS default ({name})', shell),
hidden: true, hidden: true,
env: {}, env: {},
}] }]

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService, MenuItemOptions, ProfilesService, PromptModalComponent } from 'tabby-core' import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService, MenuItemOptions, ProfilesService, PromptModalComponent, TranslateService } from 'tabby-core'
import { TerminalTabComponent } from './components/terminalTab.component' import { TerminalTabComponent } from './components/terminalTab.component'
import { UACService } from './services/uac.service' import { UACService } from './services/uac.service'
import { TerminalService } from './services/terminal.service' import { TerminalService } from './services/terminal.service'
@ -13,6 +13,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
private config: ConfigService, private config: ConfigService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private notifications: NotificationsService, private notifications: NotificationsService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -23,10 +24,10 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
} }
const items: MenuItemOptions[] = [ const items: MenuItemOptions[] = [
{ {
label: 'Save as profile', label: this.translate.instant('Save as profile'),
click: async () => { click: async () => {
const modal = this.ngbModal.open(PromptModalComponent) const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'New profile name' modal.componentInstance.prompt = this.translate.instant('New profile name')
const name = (await modal.result)?.value const name = (await modal.result)?.value
if (!name) { if (!name) {
return return
@ -44,7 +45,7 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
profile, profile,
] ]
this.config.save() this.config.save()
this.notifications.info('Saved') this.notifications.info(this.translate.instant('Saved'))
}, },
}, },
] ]
@ -63,6 +64,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
private profilesService: ProfilesService, private profilesService: ProfilesService,
private terminalService: TerminalService, private terminalService: TerminalService,
private uac: UACService, private uac: UACService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -72,7 +74,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
const items: MenuItemOptions[] = [ const items: MenuItemOptions[] = [
{ {
label: 'New terminal', label: this.translate.instant('New terminal'),
click: () => { click: () => {
if (tab instanceof TerminalTabComponent) { if (tab instanceof TerminalTabComponent) {
this.profilesService.openNewTabForProfile(tab.profile) this.profilesService.openNewTabForProfile(tab.profile)
@ -82,7 +84,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
}, },
}, },
{ {
label: 'New with profile', label: this.translate.instant('New with profile'),
submenu: profiles.map(profile => ({ submenu: profiles.map(profile => ({
label: profile.name, label: profile.name,
click: async () => { click: async () => {
@ -98,7 +100,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
if (this.uac.isAvailable) { if (this.uac.isAvailable) {
items.push({ items.push({
label: 'New admin tab', label: this.translate.instant('New admin tab'),
submenu: profiles.map(profile => ({ submenu: profiles.map(profile => ({
label: profile.name, label: profile.name,
click: () => { click: () => {
@ -116,7 +118,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
if (tab instanceof TerminalTabComponent && tabHeader && this.uac.isAvailable) { if (tab instanceof TerminalTabComponent && tabHeader && this.uac.isAvailable) {
items.push({ items.push({
label: 'Duplicate as administrator', label: this.translate.instant('Duplicate as administrator'),
click: () => { click: () => {
this.profilesService.openNewTabForProfile({ this.profilesService.openNewTabForProfile({
...tab.profile, ...tab.profile,
@ -131,7 +133,7 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
if (tab instanceof TerminalTabComponent && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) { if (tab instanceof TerminalTabComponent && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
items.push({ items.push({
label: 'Focus all panes', label: this.translate.instant('Focus all panes'),
click: () => { click: () => {
tab.focusAllPanes() tab.focusAllPanes()
}, },

View File

@ -1,16 +1,16 @@
.d-flex.mb-3 .d-flex.mb-3
h3 Plugins h3(translate) Plugins
button.btn.btn-secondary.btn-sm.ml-auto((click)='openPluginsFolder()') button.btn.btn-secondary.btn-sm.ml-auto((click)='openPluginsFolder()')
i.fas.fa-folder i.fas.fa-folder
span Plugins folder span(translate) Plugins folder
.alert.alert-danger(*ngIf='errorMessage') .alert.alert-danger(*ngIf='errorMessage')
strong Error in {{erroredPlugin}}: strong(translate, [translateParams]='{plugin: erroredPlugin}') Error in {plugin}:
pre {{errorMessage}} pre {{errorMessage}}
ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav') ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Available a(ngbNavLink, translate) Available
ng-template(ngbNavContent) ng-template(ngbNavContent)
.input-group.mb-3.mt-3 .input-group.mb-3.mt-3
.input-group-prepend .input-group-prepend
@ -40,14 +40,14 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
) )
i.fas.fa-fw.fa-cloud-download(*ngIf='busy.get(plugin.name) != BusyState.Installing') i.fas.fa-fw.fa-cloud-download(*ngIf='busy.get(plugin.name) != BusyState.Installing')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing') i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing')
span.ml-2 Get span.ml-2(translate) Get
button.btn.btn-secondary.btn-block.justify-content-center( button.btn.btn-secondary.btn-block.justify-content-center(
*ngIf='plugin.homepage', *ngIf='plugin.homepage',
(click)='showPluginHomepage(plugin)' (click)='showPluginHomepage(plugin)'
) )
i.fas.fa-fw.fa-external-link-alt i.fas.fa-fw.fa-external-link-alt
span.ml-2 Homepage span.ml-2(translate) Homepage
.col-8 .col-8
ng-container(*ngTemplateOutlet='pluginInfo; context: { plugin }') ng-container(*ngTemplateOutlet='pluginInfo; context: { plugin }')
@ -55,7 +55,7 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
.mt-2 {{plugin.description}} .mt-2 {{plugin.description}}
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Installed a(ngbNavLink, translate) Installed
ng-template(ngbNavContent) ng-template(ngbNavContent)
ngb-accordion.mb-4([closeOthers]='true') ngb-accordion.mb-4([closeOthers]='true')
ng-container(*ngFor='let plugin of pluginManager.installedPlugins') ng-container(*ngFor='let plugin of pluginManager.installedPlugins')
@ -64,8 +64,8 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
.text-left.mr-auto .text-left.mr-auto
div div
strong {{plugin.name}} strong {{plugin.name}}
small.text-muted.ml-2(*ngIf='plugin.isBuiltin') Built-in small.text-muted.ml-2(*ngIf='plugin.isBuiltin', translate) Built-in
small.text-warning.ml-2(*ngIf='!isPluginEnabled(plugin)') Disabled small.text-warning.ml-2(*ngIf='!isPluginEnabled(plugin)', translate) Disabled
small.d-block.text-muted {{plugin.description}} small.d-block.text-muted {{plugin.description}}
button.btn.btn-primary.ml-2( button.btn.btn-primary.ml-2(
@ -75,7 +75,7 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
) )
i.fas.fa-fw.fa-arrow-up(*ngIf='busy.get(plugin.name) != BusyState.Installing') i.fas.fa-fw.fa-arrow-up(*ngIf='busy.get(plugin.name) != BusyState.Installing')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing') i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Installing')
span Upgrade to {{knownUpgrades[plugin.name].version}} span(translate, [translateParams]='{version: knownUpgrades[plugin.name].version}') Upgrade to {version}
ng-template(ngbPanelContent) ng-template(ngbPanelContent)
.row .row
@ -83,12 +83,14 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
button.btn.btn-warning.btn-block.justify-content-center( button.btn.btn-warning.btn-block.justify-content-center(
(click)='togglePlugin(plugin)', (click)='togglePlugin(plugin)',
*ngIf='isPluginEnabled(plugin)', *ngIf='isPluginEnabled(plugin)',
[disabled]='!canDisablePlugin(plugin)' [disabled]='!canDisablePlugin(plugin)',
translate
) Disable ) Disable
button.btn.btn-success.btn-block.justify-content-center( button.btn.btn-success.btn-block.justify-content-center(
(click)='togglePlugin(plugin)', (click)='togglePlugin(plugin)',
*ngIf='canDisablePlugin(plugin) && !isPluginEnabled(plugin)' *ngIf='canDisablePlugin(plugin) && !isPluginEnabled(plugin)',
translate
) Enable ) Enable
button.btn.btn-danger.btn-block.justify-content-center( button.btn.btn-danger.btn-block.justify-content-center(
@ -98,26 +100,25 @@ ul.nav-tabs.mb-2(ngbNav, #nav='ngbNav')
) )
i.fas.fa-fw.fa-trash(*ngIf='busy.get(plugin.name) != BusyState.Uninstalling') i.fas.fa-fw.fa-trash(*ngIf='busy.get(plugin.name) != BusyState.Uninstalling')
i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Uninstalling') i.fas.fa-fw.fa-circle-notch.fa-spin(*ngIf='busy.get(plugin.name) == BusyState.Uninstalling')
span Uninstall span(translate) Uninstall
.col-8 .col-8
ng-container(*ngTemplateOutlet='pluginInfo; context: { plugin }') ng-container(*ngTemplateOutlet='pluginInfo; context: { plugin }')
.mt-2 {{plugin.description}} .mt-2 {{plugin.description}}
ng-template(#pluginInfo, let-plugin='plugin') ng-template(#pluginInfo, let-plugin='plugin')
.row.align-items-center .row.align-items-center
.col-4 .col-4
strong Version strong(translate) Version
.col-8 .col-8
span {{plugin.version}} span {{plugin.version}}
.row.align-items-center .row.align-items-center
.col-4 .col-4
strong Author strong(translate) Author
.col-8 .col-8
.badge.badge-success(*ngIf='plugin.isOfficial') .badge.badge-success(*ngIf='plugin.isOfficial')
i.fas.fa-check i.fas.fa-check
span.ml-1 Official span.ml-1(translate) Official
a.btn.btn-link.px-0.w-auto((click)='showPluginInfo(plugin)', *ngIf='!plugin.isOfficial') a.btn.btn-link.px-0.w-auto((click)='showPluginInfo(plugin)', *ngIf='!plugin.isOfficial')
span {{plugin.author}} span {{plugin.author}}
i.fas.fa-fw.fa-external-link-alt.ml-2 i.fas.fa-fw.fa-external-link-alt.ml-2

View File

@ -1,11 +1,11 @@
ul.nav-tabs(ngbNav, #nav='ngbNav') ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) General a(ngbNavLink, translate) General
ng-template(ngbNavContent) ng-template(ngbNavContent)
.row .row
.col-6(ng:if='hostApp.platform !== Platform.Web') .col-6(ng:if='hostApp.platform !== Platform.Web')
.form-group .form-group
label Device label(translate) Device
input.form-control( input.form-control(
type='text', type='text',
alwaysVisibleTypeahead, alwaysVisibleTypeahead,
@ -16,7 +16,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.col-6 .col-6
.form-group .form-group
label Baud Rate label(translate) Baud rate
input.form-control( input.form-control(
type='number', type='number',
alwaysVisibleTypeahead, alwaysVisibleTypeahead,
@ -28,11 +28,11 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
stream-processing-settings([options]='profile.options') stream-processing-settings([options]='profile.options')
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Advanced a(ngbNavLink, translate) Advanced
ng-template(ngbNavContent) ng-template(ngbNavContent)
.form-line .form-line
.header .header
.title Data bits .title(translate) Data bits
input.form-control( input.form-control(
type='number', type='number',
placeholder='8', placeholder='8',
@ -41,7 +41,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Stop bits .title(translate) Stop bits
input.form-control( input.form-control(
type='number', type='number',
placeholder='1', placeholder='1',
@ -50,12 +50,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Parity .title(translate) Parity
select.form-control( select.form-control(
type='text', type='text',
[(ngModel)]='profile.options.parity' [(ngModel)]='profile.options.parity'
) )
option(value='none') None option(value='none', translate) None
option(value='even') Even option(value='even') Even
option(value='odd') Odd option(value='odd') Odd
option(value='mark', ng:if='hostApp.platform === Platform.Windows') Mark option(value='mark', ng:if='hostApp.platform === Platform.Windows') Mark
@ -83,12 +83,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Slow feed .title(translate) Slow feed
.description Sends data one byte at a time .description(translate) Sends data one byte at a time
toggle([(ngModel)]='profile.options.slowFeed') toggle([(ngModel)]='profile.options.slowFeed')
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Login scripts a(ngbNavLink, translate) Login scripts
ng-template(ngbNavContent) ng-template(ngbNavContent)
login-scripts-settings([options]='profile.options') login-scripts-settings([options]='profile.options')

View File

@ -9,8 +9,8 @@
.mr-auto .mr-auto
button.btn.btn-sm.btn-link.mr-3((click)='changeBaudRate()', *ngIf='session && session.open && hostApp.platform !== Platform.Web') button.btn.btn-sm.btn-link.mr-3((click)='changeBaudRate()', *ngIf='session && session.open && hostApp.platform !== Platform.Web')
span Change baud rate span(translate) Change baud rate
button.btn.btn-sm.btn-link((click)='reconnect()', *ngIf='!session || !session.open') button.btn.btn-sm.btn-link((click)='reconnect()', *ngIf='!session || !session.open')
i.fas.fa-redo i.fas.fa-redo
span Reconnect span(translate) Reconnect

View File

@ -2,7 +2,7 @@
import colors from 'ansi-colors' import colors from 'ansi-colors'
import { Component, Injector } from '@angular/core' import { Component, Injector } from '@angular/core'
import { first } from 'rxjs' import { first } from 'rxjs'
import { Platform, SelectorService } from 'tabby-core' import { Platform, SelectorService, TranslateService } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal' import { BaseTerminalTabComponent } from 'tabby-terminal'
import { SerialSession, BAUD_RATES, SerialProfile } from '../api' import { SerialSession, BAUD_RATES, SerialProfile } from '../api'
@ -23,6 +23,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
constructor ( constructor (
injector: Injector, injector: Injector,
private selector: SelectorService, private selector: SelectorService,
private translate: TranslateService,
) { ) {
super(injector) super(injector)
this.enableToolbar = true this.enableToolbar = true
@ -67,14 +68,13 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
const session = new SerialSession(this.injector, this.profile) const session = new SerialSession(this.injector, this.profile)
this.setSession(session) this.setSession(session)
this.write(`Connecting to `)
this.startSpinner('Connecting') this.startSpinner(this.translate.instant('Connecting'))
try { try {
await this.session!.start() await this.session!.start()
this.stopSpinner() this.stopSpinner()
session.emitServiceMessage('Port opened') session.emitServiceMessage(this.translate.instant('Port opened'))
} catch (e) { } catch (e) {
this.stopSpinner() this.stopSpinner()
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n') this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
@ -89,7 +89,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
this.session?.resize(this.size.columns, this.size.rows) this.session?.resize(this.size.columns, this.size.rows)
}) })
this.attachSessionHandler(this.session!.destroyed$, () => { this.attachSessionHandler(this.session!.destroyed$, () => {
this.write('Press any key to reconnect\r\n') this.write(this.translate.instant('Press any key to reconnect') + '\r\n')
this.input$.pipe(first()).subscribe(() => { this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open) { if (!this.session?.open) {
this.reconnect() this.reconnect()
@ -114,9 +114,12 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
} }
async changeBaudRate () { async changeBaudRate () {
const rate = await this.selector.show('Baud rate', BAUD_RATES.map(x => ({ const rate = await this.selector.show(
name: x.toString(), result: x, this.translate.instant('Baud rate'),
}))) BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})),
)
this.serialPort.update({ baudRate: rate }) this.serialPort.update({ baudRate: rate })
this.profile!.options.baudrate = rate this.profile!.options.baudrate = rate
} }

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core' import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
@ -7,14 +7,16 @@ export class SerialHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [ hotkeys: HotkeyDescription[] = [
{ {
id: 'serial', id: 'serial',
name: 'Show Serial connections', name: this.translate.instant('Show Serial connections'),
}, },
{ {
id: 'restart-serial-session', id: 'restart-serial-session',
name: 'Restart current serial session', name: this.translate.instant('Restart current serial session'),
}, },
] ]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> { async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys return this.hotkeys
} }

View File

@ -3,7 +3,7 @@ import SerialPort from 'serialport'
import WSABinding from 'serialport-binding-webserialapi' import WSABinding from 'serialport-binding-webserialapi'
import deepClone from 'clone-deep' import deepClone from 'clone-deep'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ProfileProvider, NewTabParameters, SelectorService, HostAppService, Platform } from 'tabby-core' import { ProfileProvider, NewTabParameters, SelectorService, HostAppService, Platform, TranslateService } from 'tabby-core'
import { SerialProfileSettingsComponent } from './components/serialProfileSettings.component' import { SerialProfileSettingsComponent } from './components/serialProfileSettings.component'
import { SerialTabComponent } from './components/serialTab.component' import { SerialTabComponent } from './components/serialTab.component'
import { SerialService } from './services/serial.service' import { SerialService } from './services/serial.service'
@ -12,7 +12,7 @@ import { BAUD_RATES, SerialProfile } from './api'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SerialProfilesService extends ProfileProvider<SerialProfile> { export class SerialProfilesService extends ProfileProvider<SerialProfile> {
id = 'serial' id = 'serial'
name = 'Serial' name = this.translate.instant('Serial')
settingsComponent = SerialProfileSettingsComponent settingsComponent = SerialProfileSettingsComponent
configDefaults = { configDefaults = {
options: { options: {
@ -38,6 +38,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
private selector: SelectorService, private selector: SelectorService,
private serial: SerialService, private serial: SerialService,
private hostApp: HostAppService, private hostApp: HostAppService,
private translate: TranslateService,
) { ) {
super() super()
if (hostApp.platform === Platform.Web) { if (hostApp.platform === Platform.Web) {
@ -51,7 +52,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
{ {
id: `serial:web`, id: `serial:web`,
type: 'serial', type: 'serial',
name: 'Serial connection', name: this.translate.instant('Serial connection'),
icon: 'fas fa-microchip', icon: 'fas fa-microchip',
isBuiltin: true, isBuiltin: true,
} as SerialProfile, } as SerialProfile,
@ -62,7 +63,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
{ {
id: `serial:template`, id: `serial:template`,
type: 'serial', type: 'serial',
name: 'Serial connection', name: this.translate.instant('Serial connection'),
icon: 'fas fa-microchip', icon: 'fas fa-microchip',
isBuiltin: true, isBuiltin: true,
isTemplate: true, isTemplate: true,
@ -70,7 +71,9 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
...(await this.serial.listPorts()).map(p => ({ ...(await this.serial.listPorts()).map(p => ({
id: `serial:port-${slugify(p.name).replace('.', '-')}`, id: `serial:port-${slugify(p.name).replace('.', '-')}`,
type: 'serial', type: 'serial',
name: p.description ? `Serial: ${p.description}` : 'Serial', name: p.description ?
this.translate.instant('Serial: {description}', p) :
this.translate.instant('Serial'),
icon: 'fas fa-microchip', icon: 'fas fa-microchip',
isBuiltin: true, isBuiltin: true,
options: { options: {
@ -83,9 +86,12 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
async getNewTabParameters (profile: SerialProfile): Promise<NewTabParameters<SerialTabComponent>> { async getNewTabParameters (profile: SerialProfile): Promise<NewTabParameters<SerialTabComponent>> {
if (!profile.options.baudrate) { if (!profile.options.baudrate) {
profile = deepClone(profile) profile = deepClone(profile)
profile.options.baudrate = await this.selector.show('Baud rate', BAUD_RATES.map(x => ({ profile.options.baudrate = await this.selector.show(
name: x.toString(), result: x, this.translate.instant('Baud rate'),
}))) BAUD_RATES.map(x => ({
name: x.toString(), result: x,
})),
)
} }
return { return {
type: SerialTabComponent, type: SerialTabComponent,

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService } from 'tabby-core' import { ToolbarButtonProvider, ToolbarButton, AppService, HostAppService, HotkeysService, TranslateService } from 'tabby-core'
import { SettingsTabComponent } from './components/settingsTab.component' import { SettingsTabComponent } from './components/settingsTab.component'
@ -10,6 +10,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
hostApp: HostAppService, hostApp: HostAppService,
hotkeys: HotkeysService, hotkeys: HotkeysService,
private app: AppService, private app: AppService,
private translate: TranslateService,
) { ) {
super() super()
hostApp.settingsUIRequest$.subscribe(() => this.open()) hostApp.settingsUIRequest$.subscribe(() => this.open())
@ -24,7 +25,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
provide (): ToolbarButton[] { provide (): ToolbarButton[] {
return [{ return [{
icon: require('./icons/cog.svg'), icon: require('./icons/cog.svg'),
title: 'Settings', title: this.translate.instant('Settings'),
touchBarNSImage: 'NSTouchBarComposeTemplate', touchBarNSImage: 'NSTouchBarComposeTemplate',
weight: 10, weight: 10,
click: (): void => this.open(), click: (): void => this.open(),

View File

@ -1,12 +1,12 @@
h3.mb-3 Config sync h3.mb-3(translate) Config sync
ul.nav-tabs(ngbNav, #nav='ngbNav') ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Sync a(ngbNavLink, translate) Sync
ng-template(ngbNavContent) ng-template(ngbNavContent)
.form-line .form-line
.header .header
.title Sync host .title(translate) Sync host
.input-group.w-50 .input-group.w-50
input.form-control( input.form-control(
@ -20,8 +20,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Secret sync token .title(translate) Secret sync token
.description Get it from the Tabby Web settings window .description(translate) Get it from the Tabby Web settings window
.input-group .input-group
input.form-control( input.form-control(
@ -38,16 +38,16 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
ng-container(*ngIf='config.store.configSync.token') ng-container(*ngIf='config.store.configSync.token')
.alert.alert-danger(*ngIf='connectionSuccessful === false') .alert.alert-danger(*ngIf='connectionSuccessful === false')
i.fas.fa-exclamation-triangle i.fas.fa-exclamation-triangle
span.ml-2 Connection failed: {{connectionError}} span.ml-2(translate, [translateParams]='{error: connectionError}') Connection failed: {error}
ng-container(*ngIf='connectionSuccessful') ng-container(*ngIf='connectionSuccessful')
.form-line .form-line
.header .header
.title Configs .title(translate) Configs
div(*ngIf='configs === null') div(*ngIf='configs === null')
i.fas.fa-fw.fa-circle-notch.fa-spin i.fas.fa-fw.fa-circle-notch.fa-spin
span.ml-2 Loading configs... span.ml-2(translate) Loading configs...
ng-container(*ngIf='configs !== null') ng-container(*ngIf='configs !== null')
.list-group-light .list-group-light
@ -59,34 +59,34 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
i.fas.fa-fw.fa-file i.fas.fa-fw.fa-file
.ml-2.d-flex.flex-column.align-items-start .ml-2.d-flex.flex-column.align-items-start
div {{cfg.name}} div {{cfg.name}}
small.text-muted Modified on {{cfg.modified_at|date:'medium'}} small.text-muted(translate, [translateParams]='{date: cfg.modified_at|date:"medium"}') Modified on {date}
.mr-auto .mr-auto
button.btn.btn-link.ml-1( button.btn.btn-link.ml-1(
(click)='uploadAndSync(cfg)', (click)='uploadAndSync(cfg)',
[class.hover-reveal]='!isActiveConfig(cfg)' [class.hover-reveal]='!isActiveConfig(cfg)'
) )
i.fas.fa-arrow-up i.fas.fa-arrow-up
span.ml-2(*ngIf='isActiveConfig(cfg)') Upload span.ml-2(*ngIf='isActiveConfig(cfg)', translate) Upload
span.ml-2(*ngIf='!isActiveConfig(cfg)') Replace span.ml-2(*ngIf='!isActiveConfig(cfg)', translate) Replace
button.btn.btn-link.ml-1( button.btn.btn-link.ml-1(
(click)='downloadAndSync(cfg)', (click)='downloadAndSync(cfg)',
[class.hover-reveal]='!isActiveConfig(cfg)' [class.hover-reveal]='!isActiveConfig(cfg)'
) )
i.fas.fa-arrow-down i.fas.fa-arrow-down
span.ml-2 Download span.ml-2(translate) Download
a.list-group-item.list-group-item-action.d-flex.align-items-center( a.list-group-item.list-group-item-action.d-flex.align-items-center(
href='#', href='#',
(click)='uploadAsNew()' (click)='uploadAsNew()'
) )
i.fas.fa-fw i.fas.fa-fw
i.fas.fa-fw.fa-cloud-upload-alt i.fas.fa-fw.fa-cloud-upload-alt
.ml-2 Upload as a new config .ml-2(translate) Upload as a new config
ng-container(*ngIf='hasMatchingRemoteConfig()') ng-container(*ngIf='hasMatchingRemoteConfig()')
.form-line .form-line
.header .header
.title Sync automatically .title(translate) Sync automatically
.description Automatically upload changes and check for updates every minute .description(translate) Automatically upload changes and check for updates every minute
toggle( toggle(
[(ngModel)]='config.store.configSync.auto', [(ngModel)]='config.store.configSync.auto',
@ -94,11 +94,11 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
) )
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Advanced a(ngbNavLink, translate) Advanced
ng-template(ngbNavContent) ng-template(ngbNavContent)
.form-line .form-line
.header .header
.title Sync hotkeys .title(translate) Sync hotkeys
toggle( toggle(
[(ngModel)]='config.store.configSync.parts.hotkeys', [(ngModel)]='config.store.configSync.parts.hotkeys',
(ngModelChange)='config.save()', (ngModelChange)='config.save()',
@ -106,7 +106,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Sync window settings .title(translate) Sync window settings
toggle( toggle(
[(ngModel)]='config.store.configSync.parts.appearance', [(ngModel)]='config.store.configSync.parts.appearance',
(ngModelChange)='config.save()', (ngModelChange)='config.save()',
@ -114,7 +114,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Sync Vault .title(translate) Sync Vault
toggle( toggle(
[(ngModel)]='config.store.configSync.parts.vault', [(ngModel)]='config.store.configSync.parts.vault',
(ngModelChange)='config.save()', (ngModelChange)='config.save()',

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, HostBinding } from '@angular/core' import { Component, HostBinding } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService } from 'tabby-core' import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService, TranslateService } from 'tabby-core'
import { Config, ConfigSyncService } from '../services/configSync.service' import { Config, ConfigSyncService } from '../services/configSync.service'
@ -24,6 +24,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
private hostApp: HostAppService, private hostApp: HostAppService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private notifications: NotificationsService, private notifications: NotificationsService,
private translate: TranslateService,
) { ) {
super() super()
} }
@ -54,9 +55,9 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
} }
async uploadAsNew () { async uploadAsNew () {
let name = `New config on ${this.hostApp.platform}` let name = this.translate.instant('New config on {platform}', this.hostApp)
const modal = this.ngbModal.open(PromptModalComponent) const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'Name for the new config' modal.componentInstance.prompt = this.translate.instant('Name for the new config')
modal.componentInstance.value = name modal.componentInstance.value = name
name = (await modal.result)?.value name = (await modal.result)?.value
if (!name) { if (!name) {
@ -72,8 +73,11 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
if (this.config.store.configSync.configID !== cfg.id) { if (this.config.store.configSync.configID !== cfg.id) {
if ((await this.platform.showMessageBox({ if ((await this.platform.showMessageBox({
type: 'warning', type: 'warning',
message: 'Overwrite the config on the remote side and start syncing?', message: this.translate.instant('Overwrite the config on the remote side and start syncing?'),
buttons: ['Overwrite remote and sync', 'Cancel'], buttons: [
this.translate.instant('Overwrite remote and sync'),
this.translate.instant('Cancel'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
})).response === 1) { })).response === 1) {
@ -83,14 +87,17 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
this.configSync.setConfig(cfg) this.configSync.setConfig(cfg)
await this.configSync.upload() await this.configSync.upload()
this.loadConfigs() this.loadConfigs()
this.notifications.info('Config uploaded') this.notifications.info(this.translate.instant('Config uploaded'))
} }
async downloadAndSync (cfg: Config) { async downloadAndSync (cfg: Config) {
if ((await this.platform.showMessageBox({ if ((await this.platform.showMessageBox({
type: 'warning', type: 'warning',
message: 'Overwrite the local config and start syncing?', message: this.translate.instant('Overwrite the local config and start syncing?'),
buttons: ['Overwrite local and sync', 'Cancel'], buttons: [
this.translate.instant('Overwrite local and sync'),
this.translate.instant('Cancel'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
})).response === 1) { })).response === 1) {
@ -98,7 +105,7 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
} }
this.configSync.setConfig(cfg) this.configSync.setConfig(cfg)
await this.configSync.download() await this.configSync.download()
this.notifications.info('Config downloaded') this.notifications.info(this.translate.instant('Config downloaded'))
} }
hasMatchingRemoteConfig () { hasMatchingRemoteConfig () {

View File

@ -2,13 +2,13 @@
h3.m-0 {{profile.name}} h3.m-0 {{profile.name}}
.modal-header(*ngIf='defaultsMode') .modal-header(*ngIf='defaultsMode')
h3.m-0 Defaults for {{profileProvider.name}} h3.m-0(translate, [translateParams]='{type: profileProvider.name}') Defaults for {type}
.modal-body .modal-body
.row .row
.col-12.col-lg-4 .col-12.col-lg-4
.form-group(*ngIf='!defaultsMode') .form-group(*ngIf='!defaultsMode')
label Name label(translate) Name
input.form-control( input.form-control(
type='text', type='text',
autofocus, autofocus,
@ -16,7 +16,7 @@
) )
.form-group(*ngIf='!defaultsMode') .form-group(*ngIf='!defaultsMode')
label Group label(translate) Group
input.form-control( input.form-control(
type='text', type='text',
alwaysVisibleTypeahead, alwaysVisibleTypeahead,
@ -26,7 +26,7 @@
) )
.form-group(*ngIf='!defaultsMode') .form-group(*ngIf='!defaultsMode')
label Icon label(translate) Icon
.input-group .input-group
input.form-control( input.form-control(
type='text', type='text',
@ -45,7 +45,7 @@
.form-line .form-line
.header .header
.title Color .title(translate) Color
input.form-control.w-50( input.form-control.w-50(
type='text', type='text',
[(ngModel)]='profile.color', [(ngModel)]='profile.color',
@ -56,8 +56,8 @@
.form-line .form-line
.header .header
.title Disable dynamic tab title .title(translate) Disable dynamic tab title
.description Connection name will be used instead .description(translate) Connection name will be used instead
toggle([(ngModel)]='profile.disableDynamicTitle') toggle([(ngModel)]='profile.disableDynamicTitle')
.mb-4 .mb-4
@ -66,5 +66,5 @@
ng-template(#placeholder) ng-template(#placeholder)
.modal-footer .modal-footer
button.btn.btn-primary((click)='save()') Save button.btn.btn-primary((click)='save()', translate) Save
button.btn.btn-danger((click)='cancel()') Cancel button.btn.btn-danger((click)='cancel()', translate) Cancel

View File

@ -1,5 +1,5 @@
.modal-header .modal-header
h5 Press the key now h5(translate) Press the key now
.modal-body .modal-body
.input .input
@ -9,4 +9,4 @@
div([style.width]='timeoutProgress + "%"') div([style.width]='timeoutProgress + "%"')
.modal-footer .modal-footer
button.btn.btn-primary((click)='close()') Cancel button.btn.btn-primary((click)='close()', translate) Cancel

View File

@ -1,4 +1,4 @@
h3.mb-3 Hotkeys h3.mb-3(translate) Hotkeys
.input-group.mb-4 .input-group.mb-4
.input-group-prepend .input-group-prepend

View File

@ -3,4 +3,4 @@
.stroke(*ngFor='let stroke of item') {{stroke}} .stroke(*ngFor='let stroke of item') {{stroke}}
.remove((click)='removeItem(item)') &times; .remove((click)='removeItem(item)') &times;
.add((click)='addItem()') Add... .add((click)='addItem()', translate) Add...

View File

@ -1,12 +1,12 @@
h3.mb-3 Profiles h3.mb-3(translate) Profiles
ul.nav-tabs(ngbNav, #nav='ngbNav') ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Profiles a(ngbNavLink, translate) Profiles
ng-template(ngbNavContent) ng-template(ngbNavContent)
.form-line .form-line
.header .header
.title Default profile for new tabs .title(translate) Default profile for new tabs
select.form-control( select.form-control(
[(ngModel)]='config.store.terminal.profile', [(ngModel)]='config.store.terminal.profile',
@ -30,7 +30,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
button.btn.btn-primary.flex-shrink-0.ml-3((click)='newProfile()') button.btn.btn-primary.flex-shrink-0.ml-3((click)='newProfile()')
i.fas.fa-fw.fa-plus i.fas.fa-fw.fa-plus
| New profile span(translate) New profile
.list-group.mt-3.mb-3 .list-group.mt-3.mb-3
ng-container(*ngFor='let group of profileGroups') ng-container(*ngFor='let group of profileGroups')
@ -40,7 +40,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
) )
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed') .fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed')
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed') .fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed')
span.ml-3.mr-auto {{group.name || "Ungrouped"}} span.ml-3.mr-auto {{group.name || ("Ungrouped"|translate)}}
button.btn.btn-sm.btn-link.hover-reveal.ml-2( button.btn.btn-sm.btn-link.hover-reveal.ml-2(
*ngIf='group.editable && group.name', *ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); editGroup(group)' (click)='$event.stopPropagation(); editGroup(group)'
@ -88,12 +88,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.ml-1(class='badge badge-{{getTypeColorClass(profile)}}') {{getTypeLabel(profile)}} .ml-1(class='badge badge-{{getTypeColorClass(profile)}}') {{getTypeLabel(profile)}}
li(ngbNavItem) li(ngbNavItem)
a(ngbNavLink) Advanced a(ngbNavLink, translate) Advanced
ng-template(ngbNavContent) ng-template(ngbNavContent)
.form-line(*ngIf='config.store.profiles.length > 0') .form-line(*ngIf='config.store.profiles.length > 0')
.header .header
.title Show recent profiles in selector .title(translate) Show recent profiles in selector
.description Set to 0 to disable recent profiles .description(translate) Set to 0 to disable recent profiles
input.form-control( input.form-control(
type='number', type='number',
@ -105,8 +105,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line(*ngIf='config.store.profiles.length > 0') .form-line(*ngIf='config.store.profiles.length > 0')
.header .header
.title Show built-in profiles in selector .title(translate) Show built-in profiles in selector
.description If disabled, only custom profiles will show up in the profile selector .description(translate) If disabled, only custom profiles will show up in the profile selector
toggle( toggle(
[(ngModel)]='config.store.terminal.showBuiltinProfiles', [(ngModel)]='config.store.terminal.showBuiltinProfiles',
@ -115,8 +115,8 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
.form-line .form-line
.header .header
.title Default profile settings .title(translate) Default profile settings
.description These apply to all profiles of a given type .description(translate) These apply to all profiles of a given type
.list-group.mt-3.mb-3 .list-group.mt-3.mb-3
a.list-group-item.list-group-item-action( a.list-group-item.list-group-item-action(

View File

@ -3,7 +3,7 @@ import slugify from 'slugify'
import deepClone from 'clone-deep' import deepClone from 'clone-deep'
import { Component, HostBinding, Inject } from '@angular/core' import { Component, HostBinding, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider } from 'tabby-core' import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService } from 'tabby-core'
import { EditProfileModalComponent } from './editProfileModal.component' import { EditProfileModalComponent } from './editProfileModal.component'
interface ProfileGroup { interface ProfileGroup {
@ -35,6 +35,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
private selector: SelectorService, private selector: SelectorService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private platform: PlatformService, private platform: PlatformService,
private translate: TranslateService,
) { ) {
super() super()
this.profileProviders.sort((a, b) => a.name.localeCompare(b.name)) this.profileProviders.sort((a, b) => a.name.localeCompare(b.name))
@ -58,7 +59,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
const profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles] const profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles]
profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0)) profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
base = await this.selector.show( base = await this.selector.show(
'Select a base profile to use as a template', this.translate.instant('Select a base profile to use as a template'),
profiles.map(p => ({ profiles.map(p => ({
icon: p.icon, icon: p.icon,
description: this.profilesService.getDescription(p) ?? undefined, description: this.profilesService.getDescription(p) ?? undefined,
@ -72,14 +73,14 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if (base.isTemplate) { if (base.isTemplate) {
profile.name = '' profile.name = ''
} else if (!base.isBuiltin) { } else if (!base.isBuiltin) {
profile.name = `${base.name} copy` profile.name = this.translate.instant('{name} copy', base)
} }
profile.isBuiltin = false profile.isBuiltin = false
profile.isTemplate = false profile.isTemplate = false
await this.showProfileEditModal(profile) await this.showProfileEditModal(profile)
if (!profile.name) { if (!profile.name) {
const cfgProxy = this.profilesService.getConfigProxyForProfile(profile) const cfgProxy = this.profilesService.getConfigProxyForProfile(profile)
profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? `${base.name} copy` profile.name = this.profilesService.providerForProfile(profile)?.getSuggestedName(cfgProxy) ?? this.translate.instant('{name} copy', base)
} }
profile.id = `${profile.type}:custom:${slugify(profile.name)}:${uuidv4()}` profile.id = `${profile.type}:custom:${slugify(profile.name)}:${uuidv4()}`
this.config.store.profiles = [profile, ...this.config.store.profiles] this.config.store.profiles = [profile, ...this.config.store.profiles]
@ -122,8 +123,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox( if ((await this.platform.showMessageBox(
{ {
type: 'warning', type: 'warning',
message: `Delete "${profile.name}"?`, message: this.translate.instant('Delete "{name}"?', profile),
buttons: ['Delete', 'Keep'], buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
} }
@ -156,7 +160,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
this.profileGroups.sort((a, b) => a.name?.localeCompare(b.name ?? '') ?? -1) this.profileGroups.sort((a, b) => a.name?.localeCompare(b.name ?? '') ?? -1)
this.profileGroups.push({ this.profileGroups.push({
name: 'Built-in', name: this.translate.instant('Built-in'),
profiles: this.builtinProfiles, profiles: this.builtinProfiles,
editable: false, editable: false,
collapsed: false, collapsed: false,
@ -165,7 +169,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
async editGroup (group: ProfileGroup): Promise<void> { async editGroup (group: ProfileGroup): Promise<void> {
const modal = this.ngbModal.open(PromptModalComponent) const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'New name' modal.componentInstance.prompt = this.translate.instant('New name')
modal.componentInstance.value = group.name modal.componentInstance.value = group.name
const result = await modal.result const result = await modal.result
if (result) { if (result) {
@ -181,8 +185,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox( if ((await this.platform.showMessageBox(
{ {
type: 'warning', type: 'warning',
message: `Delete "${group.name}"?`, message: this.translate.instant('Delete "{name}"?', group),
buttons: ['Delete', 'Keep'], buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
} }
@ -190,8 +197,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox( if ((await this.platform.showMessageBox(
{ {
type: 'warning', type: 'warning',
message: `Delete the group's profiles?`, message: this.translate.instant(`Delete the group's profiles?`),
buttons: ['Move to "Ungrouped"', 'Delete'], buttons: [
this.translate.instant('Move to "Ungrouped"'),
this.translate.instant('Delete'),
],
defaultId: 0, defaultId: 0,
cancelId: 0, cancelId: 0,
} }
@ -224,10 +234,10 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
getTypeLabel (profile: PartialProfile<Profile>): string { getTypeLabel (profile: PartialProfile<Profile>): string {
const name = this.profilesService.providerForProfile(profile)?.name const name = this.profilesService.providerForProfile(profile)?.name
if (name === 'Local') { if (name === this.translate.instant('Local terminal')) {
return '' return ''
} }
return name ?? 'Unknown' return name ?? this.translate.instant('Unknown')
} }
getTypeColorClass (profile: PartialProfile<Profile>): string { getTypeColorClass (profile: PartialProfile<Profile>): string {

View File

@ -2,7 +2,7 @@
import axios from 'axios' import axios from 'axios'
import { marked } from 'marked' import { marked } from 'marked'
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { BaseTabComponent } from 'tabby-core' import { BaseTabComponent, TranslateService } from 'tabby-core'
export interface Release { export interface Release {
name: string name: string
@ -21,9 +21,9 @@ export class ReleaseNotesComponent extends BaseTabComponent {
releases: Release[] = [] releases: Release[] = []
lastPage = 1 lastPage = 1
constructor () { constructor (translate: TranslateService) {
super() super()
this.setTitle('Release notes') this.setTitle(translate.instant('Release notes'))
this.loadReleases(1) this.loadReleases(1)
} }

View File

@ -1,6 +1,6 @@
h3.modal-header.m-0.pb-0 Set master passphrase h3.modal-header.m-0.pb-0(translate) Set master passphrase
.modal-body .modal-body
.mb-2 You can change it later, but it's unrecoverable if forgotten. .mb-2(translate) You can change it later, but it's unrecoverable if forgotten.
.input-group .input-group
input.form-control.form-control-lg( input.form-control.form-control-lg(
[type]='showPassphrase ? "text" : "password"', [type]='showPassphrase ? "text" : "password"',
@ -16,5 +16,5 @@ h3.modal-header.m-0.pb-0 Set master passphrase
i.fas.fa-eye i.fas.fa-eye
.modal-footer .modal-footer
button.btn.btn-primary((click)='ok()') Set passphrase button.btn.btn-primary((click)='ok()', translate) Set passphrase
button.btn.btn-danger((click)='cancel()') Cancel button.btn.btn-danger((click)='cancel()', translate) Cancel

View File

@ -3,7 +3,7 @@
li(ngbNavItem='application') li(ngbNavItem='application')
a(ngbNavLink) a(ngbNavLink)
i.fas.fa-fw.fa-window-maximize.mr-2 i.fas.fa-fw.fa-window-maximize.mr-2
| Application span(translate) Application
ng-template(ngbNavContent) ng-template(ngbNavContent)
.content-box .content-box
.row .row
@ -23,59 +23,69 @@
i.fas.fa-sync( i.fas.fa-sync(
[class.fa-spin]='checkingForUpdate' [class.fa-spin]='checkingForUpdate'
) )
span Check for updates span(translate) Check for updates
button.btn.btn-info.mt-3.mb-2( button.btn.btn-info.mt-3.mb-2(
*ngIf='updateAvailable', *ngIf='updateAvailable',
(click)='updater.update()', (click)='updater.update()',
) )
i.fas.fa-sync i.fas.fa-sync
span Update span(translate) Update
.col-12.col-md-6 .col-12.col-md-6
.list-group.list-group-light.mb-5 .list-group.list-group-light.mb-5
button.list-group-item.list-group-item-action.link-card((click)='homeBase.reportBug()') button.list-group-item.list-group-item-action.link-card((click)='homeBase.reportBug()')
i.fas.fa-fw.fa-bug i.fas.fa-fw.fa-bug
div div
div Report a problem div(translate) Report a problem
small.text-muted Generate a pre-filled GitHub issue small.text-muted(translate) Generate a pre-filled GitHub issue
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openDiscussions()') button.list-group-item.list-group-item-action.link-card((click)='homeBase.openDiscussions()')
i.fas.fa-fw.fa-comments i.fas.fa-fw.fa-comments
div div
div Ask a question div(translate) Ask a question
small.text-muted On GitHub Discussions small.text-muted(translate) On GitHub Discussions
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openGitHub()') button.list-group-item.list-group-item-action.link-card((click)='homeBase.openGitHub()')
i.fab.fa-fw.fa-github i.fab.fa-fw.fa-github
div div
div GitHub div GitHub
small.text-muted Source code small.text-muted(translate) Source code
button.list-group-item.list-group-item-action.link-card((click)='showReleaseNotes()') button.list-group-item.list-group-item-action.link-card((click)='showReleaseNotes()')
i.fas.fa-fw.fa-book i.fas.fa-fw.fa-book
div div
div What's new div(translate) What's new
small.text-muted Show release notes small.text-muted(translate) Show release notes
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openTwitter()') button.list-group-item.list-group-item-action.link-card((click)='homeBase.openTwitter()')
i.fab.fa-fw.fa-twitter i.fab.fa-fw.fa-twitter
div div
div Subscribe to updates div(translate) Subscribe to updates
small.text-muted Tabby news and updates on Twitter small.text-muted(translate) Tabby news and updates on Twitter
h3 Application settings h3(translate) Application settings
.form-line
.header
.title(translate) Language
select.form-control([(ngModel)]='config.store.language', (ngModelChange)='saveConfiguration()')
option([value]='null', translate) Automatic
option(
[value]='lang.code',
*ngFor='let lang of locale.allLanguages'
) {{lang.name|translate}}
.form-line(*ngIf='platform.isShellIntegrationSupported()') .form-line(*ngIf='platform.isShellIntegrationSupported()')
.header .header
.title Shell integration .title(translate) Shell integration
.description Allows quickly opening a terminal in the selected folder .description(translate) Allows quickly opening a terminal in the selected folder
toggle([ngModel]='isShellIntegrationInstalled', (ngModelChange)='toggleShellIntegration()') toggle([ngModel]='isShellIntegrationInstalled', (ngModelChange)='toggleShellIntegration()')
.form-line(*ngIf='hostApp.platform !== Platform.Web') .form-line(*ngIf='hostApp.platform !== Platform.Web')
.header .header
.title Enable analytics .title(translate) Enable analytics
.description We're only tracking your Tabby and OS versions. .description(translate) We're only tracking your Tabby and OS versions.
toggle( toggle(
[(ngModel)]='config.store.enableAnalytics', [(ngModel)]='config.store.enableAnalytics',
(ngModelChange)='saveConfiguration(true)', (ngModelChange)='saveConfiguration(true)',
@ -83,23 +93,23 @@
.form-line(*ngIf='hostApp.platform !== Platform.Web') .form-line(*ngIf='hostApp.platform !== Platform.Web')
.header .header
.title Automatic Updates .title(translate) Automatic Updates
.description Enable automatic installation of updates when they become available. .description(translate) Enable automatic installation of updates when they become available.
toggle([(ngModel)]='config.store.enableAutomaticUpdates', (ngModelChange)='saveConfiguration()') toggle([(ngModel)]='config.store.enableAutomaticUpdates', (ngModelChange)='saveConfiguration()')
.form-line(*ngIf='hostApp.platform !== Platform.Web') .form-line(*ngIf='hostApp.platform !== Platform.Web')
.header .header
.title Debugging .title(translate) Debugging
button.btn.btn-secondary((click)='hostWindow.openDevTools()') button.btn.btn-secondary((click)='hostWindow.openDevTools()')
i.fas.fa-bug i.fas.fa-bug
span Open DevTools span(translate) Open DevTools
ng-container(*ngFor='let provider of settingsProviders') ng-container(*ngFor='let provider of settingsProviders')
li(*ngIf='provider.prioritized', [ngbNavItem]='provider.id') li(*ngIf='provider.prioritized', [ngbNavItem]='provider.id')
a(ngbNavLink) a(ngbNavLink)
i(class='fas fa-fw mr-2 fa-{{provider.icon}}') i(class='fas fa-fw mr-2 fa-{{provider.icon}}')
| {{provider.title}} span(translate) {{provider.title}}
ng-template(ngbNavContent) ng-template(ngbNavContent)
settings-tab-body([provider]='provider') settings-tab-body([provider]='provider')
@ -109,24 +119,24 @@
li(*ngIf='!provider.prioritized', [ngbNavItem]='provider.id') li(*ngIf='!provider.prioritized', [ngbNavItem]='provider.id')
a(ngbNavLink) a(ngbNavLink)
i(class='fas fa-fw mr-2 fa-{{provider.icon || "puzzle-piece"}}') i(class='fas fa-fw mr-2 fa-{{provider.icon || "puzzle-piece"}}')
| {{provider.title}} span(translate) {{provider.title}}
ng-template(ngbNavContent) ng-template(ngbNavContent)
settings-tab-body([provider]='provider') settings-tab-body([provider]='provider')
li(ngbNavItem='config-file') li(ngbNavItem='config-file')
a(ngbNavLink) a(ngbNavLink)
i.fas.fa-fw.fa-code.mr-2 i.fas.fa-fw.fa-code.mr-2
| Config file span(translate) Config file
ng-template.test(ngbNavContent) ng-template.test(ngbNavContent)
.d-flex.flex-column.w-100.h-100 .d-flex.flex-column.w-100.h-100
.h-100.d-flex .h-100.d-flex
.w-100.d-flex.flex-column .w-100.d-flex.flex-column
h3 Config file h3(translate) Config file
textarea.form-control.h-100( textarea.form-control.h-100(
[(ngModel)]='configFile' [(ngModel)]='configFile'
) )
.w-100.d-flex.flex-column(*ngIf='showConfigDefaults') .w-100.d-flex.flex-column(*ngIf='showConfigDefaults')
h3 Defaults h3(translate) Defaults
textarea.form-control.h-100( textarea.form-control.h-100(
[(ngModel)]='configDefaults', [(ngModel)]='configDefaults',
readonly readonly
@ -134,20 +144,25 @@
.mt-3.d-flex .mt-3.d-flex
button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()') button.btn.btn-primary((click)='saveConfigFile()', *ngIf='isConfigFileValid()')
i.fas.fa-check.mr-2 i.fas.fa-check.mr-2
| Save and apply span(translate) Save and apply
button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()') button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
i.fas.fa-exclamation-triangle.mr-2 i.fas.fa-exclamation-triangle.mr-2
| Invalid syntax span(translate) Invalid syntax
button.btn.btn-secondary.ml-auto( button.btn.btn-secondary.ml-auto(
(click)='showConfigDefaults = !showConfigDefaults' (click)='showConfigDefaults = !showConfigDefaults',
translate
) Show defaults ) Show defaults
button.btn.btn-secondary.ml-3( button.btn.btn-secondary.ml-3(
*ngIf='platform.getConfigPath()', *ngIf='platform.getConfigPath()',
(click)='showConfigFile()' (click)='showConfigFile()'
) )
i.fas.fa-external-link-square-alt.mr-2 i.fas.fa-external-link-square-alt.mr-2
| Show config file span(translate) Show config file
div([ngbNavOutlet]='nav') div([ngbNavOutlet]='nav')
button.btn.btn-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes button.btn.btn-warning.btn-block(
*ngIf='config.restartRequested',
(click)='restartApp()',
translate
) Restart the app to apply changes

View File

@ -12,6 +12,8 @@ import {
PlatformService, PlatformService,
HostWindowService, HostWindowService,
AppService, AppService,
LocaleService,
TranslateService,
} from 'tabby-core' } from 'tabby-core'
import { SettingsTabProvider } from '../api' import { SettingsTabProvider } from '../api'
@ -43,12 +45,14 @@ export class SettingsTabComponent extends BaseTabComponent {
public homeBase: HomeBaseService, public homeBase: HomeBaseService,
public platform: PlatformService, public platform: PlatformService,
public zone: NgZone, public zone: NgZone,
public locale: LocaleService,
private updater: UpdaterService, private updater: UpdaterService,
private app: AppService, private app: AppService,
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[], @Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
translate: TranslateService,
) { ) {
super() super()
this.setTitle('Settings') this.setTitle(translate.instant('Settings'))
this.settingsProviders = config.enabledServices(this.settingsProviders) this.settingsProviders = config.enabledServices(this.settingsProviders)
this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType()) this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType())
this.settingsProviders.sort((a, b) => a.weight - b.weight + a.title.localeCompare(b.title)) this.settingsProviders.sort((a, b) => a.weight - b.weight + a.title.localeCompare(b.title))

View File

@ -1,27 +1,27 @@
.text-center(*ngIf='!vault.isEnabled()') .text-center(*ngIf='!vault.isEnabled()')
i.fas.fa-key.fa-3x.m-3 i.fas.fa-key.fa-3x.m-3
h3.m-3 Vault is not configured h3.m-3(translate) Vault is not configured
.m-3 Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases. .m-3(translate) Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
button.btn.btn-primary.m-2((click)='enableVault()') Set master passphrase button.btn.btn-primary.m-2((click)='enableVault()', translate) Set master passphrase
div(*ngIf='vault.isEnabled()') div(*ngIf='vault.isEnabled()')
.d-flex.align-items-center.mb-3 .d-flex.align-items-center.mb-3
h3.m-0 Vault h3.m-0(translate) Vault
.d-flex.ml-auto(ngbDropdown, *ngIf='vault.isEnabled()') .d-flex.ml-auto(ngbDropdown, *ngIf='vault.isEnabled()')
button.btn.btn-secondary(ngbDropdownToggle) Options button.btn.btn-secondary(ngbDropdownToggle, translate) Options
div(ngbDropdownMenu) div(ngbDropdownMenu)
a(ngbDropdownItem, (click)='changePassphrase()') a(ngbDropdownItem, (click)='changePassphrase()')
i.fas.fa-fw.fa-key i.fas.fa-fw.fa-key
span Change the master passphrase span(translate) Change the master passphrase
a(ngbDropdownItem, (click)='disableVault()') a(ngbDropdownItem, (click)='disableVault()')
i.fas.fa-fw.fa-radiation-alt i.fas.fa-fw.fa-radiation-alt
span Erase the vault span(translate) Erase the Vault
div(*ngIf='vaultContents') div(*ngIf='vaultContents')
.text-center(*ngIf='!vaultContents.secrets.length') .text-center(*ngIf='!vaultContents.secrets.length')
i.fas.fa-empty-set.fa-3x i.fas.fa-empty-set.fa-3x
h3.m-3 Vault is empty h3.m-3(translate) Vault is empty
.list-group .list-group
.list-group-item.d-flex.align-items-center.p-1.pl-3(*ngFor='let secret of vaultContents.secrets') .list-group-item.d-flex.align-items-center.p-1.pl-3(*ngFor='let secret of vaultContents.secrets')
@ -38,30 +38,30 @@ div(*ngIf='vault.isEnabled()')
(click)='renameFile(secret)' (click)='renameFile(secret)'
) )
i.fas.fa-fw.fa-pencil-alt i.fas.fa-fw.fa-pencil-alt
span Rename span(translate) Rename
button( button(
ngbDropdownItem, ngbDropdownItem,
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE', *ngIf='secret.type === VAULT_SECRET_TYPE_FILE',
(click)='replaceFileContent(secret)' (click)='replaceFileContent(secret)'
) )
i.fas.fa-fw.fa-file-import i.fas.fa-fw.fa-file-import
span Replace span(translate) Replace
button( button(
ngbDropdownItem, ngbDropdownItem,
*ngIf='secret.type === VAULT_SECRET_TYPE_FILE', *ngIf='secret.type === VAULT_SECRET_TYPE_FILE',
(click)='exportFile(secret)' (click)='exportFile(secret)'
) )
i.fas.fa-fw.fa-file-export i.fas.fa-fw.fa-file-export
span Export span(translate) Export
button(ngbDropdownItem, (click)='removeSecret(secret)') button(ngbDropdownItem, (click)='removeSecret(secret)')
i.fas.fa-fw.fa-trash i.fas.fa-fw.fa-trash
span Delete span(translate) Delete
h3.mt-5 Options h3.mt-5(translate) Options
.form-line .form-line
.header .header
.title Encrypt config file .title(translate) Encrypt config file
.description Puts all of Tabby's configuration into the vault .description(translate) Puts all of Tabby's configuration into the vault
toggle( toggle(
[ngModel]='config.store.encrypted', [ngModel]='config.store.encrypted',
(click)='toggleConfigEncrypted()', (click)='toggleConfigEncrypted()',
@ -69,5 +69,5 @@ div(*ngIf='vault.isEnabled()')
.text-center(*ngIf='!vaultContents') .text-center(*ngIf='!vaultContents')
i.fas.fa-key.fa-3x i.fas.fa-key.fa-3x
h3.m-3 Vault is locked h3.m-3(translate) Vault is locked
button.btn.btn-primary.m-2((click)='loadVault()') Show vault contents button.btn.btn-primary.m-2((click)='loadVault()', translate) Show vault contents

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Component, HostBinding } from '@angular/core' import { Component, HostBinding } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService, ConfigService, VAULT_SECRET_TYPE_FILE, PromptModalComponent, VaultFileSecret } from 'tabby-core' import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService, ConfigService, VAULT_SECRET_TYPE_FILE, PromptModalComponent, VaultFileSecret, TranslateService } from 'tabby-core'
import { SetVaultPassphraseModalComponent } from './setVaultPassphraseModal.component' import { SetVaultPassphraseModalComponent } from './setVaultPassphraseModal.component'
@ -21,6 +21,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
public config: ConfigService, public config: ConfigService,
private platform: PlatformService, private platform: PlatformService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private translate: TranslateService,
) { ) {
super() super()
if (vault.isOpen()) { if (vault.isOpen()) {
@ -43,8 +44,11 @@ export class VaultSettingsTabComponent extends BaseComponent {
if ((await this.platform.showMessageBox( if ((await this.platform.showMessageBox(
{ {
type: 'warning', type: 'warning',
message: 'Delete vault contents?', message: this.translate.instant('Delete vault contents?'),
buttons: ['Delete', 'Keep'], buttons: [
this.translate.instant('Delete'),
this.translate.instant('Keep'),
],
defaultId: 1, defaultId: 1,
cancelId: 1, cancelId: 1,
} }
@ -77,16 +81,16 @@ export class VaultSettingsTabComponent extends BaseComponent {
getSecretLabel (secret: VaultSecret) { getSecretLabel (secret: VaultSecret) {
if (secret.type === 'ssh:password') { if (secret.type === 'ssh:password') {
return `SSH password for ${(secret as any).key.user}@${(secret as any).key.host}:${(secret as any).key.port}` return this.translate.instant('SSH password for {user}@{host}:{port}', (secret as any).key)
} }
if (secret.type === 'ssh:key-passphrase') { if (secret.type === 'ssh:key-passphrase') {
return `Passphrase for a private key with hash ${(secret as any).key.hash.substring(0, 8)}...` return this.translate.instant('Passphrase for a private key with hash {hash}...', { hash: (secret as any).key.hash.substring(0, 8) })
} }
if (secret.type === VAULT_SECRET_TYPE_FILE) { if (secret.type === VAULT_SECRET_TYPE_FILE) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return `File: ${(secret as VaultFileSecret).key.description}` return this.translate.instant('File: {description}', (secret as VaultFileSecret).key)
} }
return `Unknown secret of type ${secret.type} for ${JSON.stringify(secret.key)}` return this.translate.instant('Unknown secret of type {type} for {key}', { type: secret.type, key: JSON.stringify(secret.key) })
} }
removeSecret (secret: VaultSecret) { removeSecret (secret: VaultSecret) {
@ -111,7 +115,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
async renameFile (secret: VaultFileSecret) { async renameFile (secret: VaultFileSecret) {
const modal = this.ngbModal.open(PromptModalComponent) const modal = this.ngbModal.open(PromptModalComponent)
modal.componentInstance.prompt = 'New name' modal.componentInstance.prompt = this.translate.instant('New name')
modal.componentInstance.value = secret.key.description modal.componentInstance.value = secret.key.description
const description = (await modal.result)?.value const description = (await modal.result)?.value

View File

@ -1,8 +1,8 @@
h3.mb-3 Window h3.mb-3(translate) Window
.form-line .form-line
.header .header
.title Theme .title(translate) Theme
select.form-control( select.form-control(
[(ngModel)]='config.store.appearance.theme', [(ngModel)]='config.store.appearance.theme',
(ngModelChange)='saveConfiguration()', (ngModelChange)='saveConfiguration()',
@ -12,8 +12,8 @@ h3.mb-3 Window
.form-line(*ngIf='hostApp.platform === Platform.Web') .form-line(*ngIf='hostApp.platform === Platform.Web')
.header .header
.title Ask before closing the browser tab .title(translate) Ask before closing the browser tab
.description Prevents accidental closing .description(translate) Prevents accidental closing
toggle( toggle(
[(ngModel)]='config.store.web.preventAccidentalTabClosure', [(ngModel)]='config.store.web.preventAccidentalTabClosure',
(ngModelChange)='saveConfiguration()', (ngModelChange)='saveConfiguration()',
@ -22,9 +22,9 @@ h3.mb-3 Window
.form-line(*ngIf='platform.supportsWindowControls') .form-line(*ngIf='platform.supportsWindowControls')
.header .header
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background .title(*ngIf='hostApp.platform !== Platform.macOS', translate) Acrylic background
.title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy .title(*ngIf='hostApp.platform === Platform.macOS', translate) Vibrancy
.description Gives the window a blurred transparent background .description(translate) Gives the window a blurred transparent background
toggle( toggle(
[(ngModel)]='config.store.appearance.vibrancy', [(ngModel)]='config.store.appearance.vibrancy',
@ -33,7 +33,7 @@ h3.mb-3 Window
.form-line(*ngIf='config.store.appearance.vibrancy && isFluentVibrancySupported') .form-line(*ngIf='config.store.appearance.vibrancy && isFluentVibrancySupported')
.header .header
.title Background type .title(translate) Background type
.btn-group( .btn-group(
[(ngModel)]='config.store.appearance.vibrancyType', [(ngModel)]='config.store.appearance.vibrancyType',
(ngModelChange)='saveConfiguration()', (ngModelChange)='saveConfiguration()',
@ -45,18 +45,18 @@ h3.mb-3 Window
ngbButton, ngbButton,
[value]='"blur"' [value]='"blur"'
) )
| Blur span(translate) Blur
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"fluent"' [value]='"fluent"'
) )
| Fluent span Fluent
.form-line(*ngIf='platform.supportsWindowControls') .form-line(*ngIf='platform.supportsWindowControls')
.header .header
.title Opacity .title(translate) Opacity
input( input(
type='range', type='range',
[(ngModel)]='config.store.appearance.opacity', [(ngModel)]='config.store.appearance.opacity',
@ -68,8 +68,8 @@ h3.mb-3 Window
.form-line(*ngIf='platform.supportsWindowControls') .form-line(*ngIf='platform.supportsWindowControls')
.header .header
.title Window frame .title(translate) Window frame
.description Whether a custom window or an OS native window should be used .description(translate) Whether a custom window or an OS native window should be used
.btn-group( .btn-group(
[(ngModel)]='config.store.appearance.frame', [(ngModel)]='config.store.appearance.frame',
@ -82,28 +82,28 @@ h3.mb-3 Window
ngbButton, ngbButton,
[value]='"native"' [value]='"native"'
) )
| Native span(translate) Native
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"thin"' [value]='"thin"'
) )
| Thin span(translate) Thin
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"full"' [value]='"full"'
) )
| Full span(translate) Full
h3.mt-4 Docking h3.mt-4(translate) Docking
.form-line(*ngIf='docking') .form-line(*ngIf='docking')
.header .header
.title Dock the terminal .title(translate) Dock the terminal
.description Snaps the window to a side of the screen .description(translate) Snaps the window to a side of the screen
.btn-group( .btn-group(
[(ngModel)]='config.store.appearance.dock', [(ngModel)]='config.store.appearance.dock',
@ -116,40 +116,40 @@ h3.mt-4 Docking
ngbButton, ngbButton,
[value]='"off"' [value]='"off"'
) )
| Off span(translate) Off
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"top"' [value]='"top"'
) )
| Top span(translate) Top
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"left"' [value]='"left"'
) )
| Left span(translate) Left
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"right"' [value]='"right"'
) )
| Right span(translate) Right
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"bottom"' [value]='"bottom"'
) )
| Bottom span(translate) Bottom
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Display on .title(translate) Display on
.description Snaps the window to a side of the screen .description(translate) Snaps the window to a side of the screen
div( div(
[(ngModel)]='config.store.appearance.dockScreen', [(ngModel)]='config.store.appearance.dockScreen',
@ -162,7 +162,7 @@ h3.mt-4 Docking
ngbButton, ngbButton,
value='current' value='current'
) )
| Current span(translate) Current
label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel) label.btn.btn-secondary(*ngFor='let screen of screens', ngbButtonLabel)
input( input(
type='radio', type='radio',
@ -173,8 +173,8 @@ h3.mt-4 Docking
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Dock always on top .title(translate) Dock always on top
.description Keep docked terminal always on top .description(translate) Keep docked terminal always on top
toggle( toggle(
[(ngModel)]='config.store.appearance.dockAlwaysOnTop', [(ngModel)]='config.store.appearance.dockAlwaysOnTop',
(ngModelChange)='saveConfiguration(); docking.dock()', (ngModelChange)='saveConfiguration(); docking.dock()',
@ -182,7 +182,7 @@ h3.mt-4 Docking
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Docked terminal size .title(translate) Docked terminal size
input( input(
type='range', type='range',
[(ngModel)]='config.store.appearance.dockFill', [(ngModel)]='config.store.appearance.dockFill',
@ -194,7 +194,7 @@ h3.mt-4 Docking
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Docked terminal space .title(translate) Docked terminal space
input( input(
type='range', type='range',
[(ngModel)]='config.store.appearance.dockSpace', [(ngModel)]='config.store.appearance.dockSpace',
@ -206,18 +206,18 @@ h3.mt-4 Docking
.ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Hide dock on blur .title(translate) Hide dock on blur
.description Hides the docked terminal when you click away. .description(translate) Hides the docked terminal when you click away.
toggle( toggle(
[(ngModel)]='config.store.appearance.dockHideOnBlur', [(ngModel)]='config.store.appearance.dockHideOnBlur',
(ngModelChange)='saveConfiguration(); ', (ngModelChange)='saveConfiguration(); ',
) )
h3.mt-4 Tabs h3.mt-4(translate) Tabs
.form-line .form-line
.header .header
.title Tabs location .title(translate) Tabs location
.btn-group( .btn-group(
[(ngModel)]='config.store.appearance.tabsLocation', [(ngModel)]='config.store.appearance.tabsLocation',
(ngModelChange)='saveConfiguration()', (ngModelChange)='saveConfiguration()',
@ -229,32 +229,32 @@ h3.mt-4 Tabs
ngbButton, ngbButton,
[value]='"top"' [value]='"top"'
) )
| Top span(translate) Top
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"bottom"' [value]='"bottom"'
) )
| Bottom span(translate) Bottom
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"left"' [value]='"left"'
) )
| Left span(translate) Left
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='"right"' [value]='"right"'
) )
| Right span(translate) Right
.form-line .form-line
.header .header
.title Tabs width .title(translate) Tabs width
.btn-group( .btn-group(
[(ngModel)]='config.store.appearance.flexTabs', [(ngModel)]='config.store.appearance.flexTabs',
(ngModelChange)='saveConfiguration()', (ngModelChange)='saveConfiguration()',
@ -266,18 +266,18 @@ h3.mt-4 Tabs
ngbButton, ngbButton,
[value]='true' [value]='true'
) )
| Dynamic span(translate) Dynamic
label.btn.btn-secondary(ngbButtonLabel) label.btn.btn-secondary(ngbButtonLabel)
input( input(
type='radio', type='radio',
ngbButton, ngbButton,
[value]='false' [value]='false'
) )
| Fixed span(translate) Fixed
.form-line .form-line
.header .header
.title Hide tab index .title(translate) Hide tab index
toggle( toggle(
[(ngModel)]='config.store.terminal.hideTabIndex', [(ngModel)]='config.store.terminal.hideTabIndex',
@ -286,7 +286,7 @@ h3.mt-4 Tabs
.form-line .form-line
.header .header
.title Hide tab close button .title(translate) Hide tab close button
toggle( toggle(
[(ngModel)]='config.store.terminal.hideCloseButton', [(ngModel)]='config.store.terminal.hideCloseButton',
@ -297,8 +297,8 @@ h3.mt-4 Hacks
.form-line .form-line
.header .header
.title Disable GPU acceleration .title(translate) Disable GPU acceleration
.description Tick this if you're experiencing aliasing, ghosting or other visual issues .description(translate) Tick this if you're experiencing aliasing, ghosting or other visual issues
toggle( toggle(
[(ngModel)]='config.store.hacks.disableGPU', [(ngModel)]='config.store.hacks.disableGPU',

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { HotkeyDescription, HotkeyProvider } from 'tabby-core' import { HotkeyDescription, HotkeyProvider, TranslateService } from 'tabby-core'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
@ -7,10 +7,12 @@ export class SettingsHotkeyProvider extends HotkeyProvider {
hotkeys: HotkeyDescription[] = [ hotkeys: HotkeyDescription[] = [
{ {
id: 'settings', id: 'settings',
name: 'Open Settings', name: this.translate.instant('Open Settings'),
}, },
] ]
constructor (private translate: TranslateService) { super() }
async provide (): Promise<HotkeyDescription[]> { async provide (): Promise<HotkeyDescription[]> {
return this.hotkeys return this.hotkeys
} }

View File

@ -5,13 +5,16 @@ import { WindowSettingsTabComponent } from './components/windowSettingsTab.compo
import { VaultSettingsTabComponent } from './components/vaultSettingsTab.component' import { VaultSettingsTabComponent } from './components/vaultSettingsTab.component'
import { ConfigSyncSettingsTabComponent } from './components/configSyncSettingsTab.component' import { ConfigSyncSettingsTabComponent } from './components/configSyncSettingsTab.component'
import { ProfilesSettingsTabComponent } from './components/profilesSettingsTab.component' import { ProfilesSettingsTabComponent } from './components/profilesSettingsTab.component'
import { TranslateService } from 'tabby-core'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class HotkeySettingsTabProvider extends SettingsTabProvider { export class HotkeySettingsTabProvider extends SettingsTabProvider {
id = 'hotkeys' id = 'hotkeys'
icon = 'keyboard' icon = 'keyboard'
title = 'Hotkeys' title = this.translate.instant('Hotkeys')
constructor (private translate: TranslateService) { super() }
getComponentType (): any { getComponentType (): any {
return HotkeySettingsTabComponent return HotkeySettingsTabComponent
@ -24,7 +27,9 @@ export class HotkeySettingsTabProvider extends SettingsTabProvider {
export class WindowSettingsTabProvider extends SettingsTabProvider { export class WindowSettingsTabProvider extends SettingsTabProvider {
id = 'window' id = 'window'
icon = 'window-maximize' icon = 'window-maximize'
title = 'Window' title = this.translate.instant('Window')
constructor (private translate: TranslateService) { super() }
getComponentType (): any { getComponentType (): any {
return WindowSettingsTabComponent return WindowSettingsTabComponent
@ -50,9 +55,11 @@ export class VaultSettingsTabProvider extends SettingsTabProvider {
export class ProfilesSettingsTabProvider extends SettingsTabProvider { export class ProfilesSettingsTabProvider extends SettingsTabProvider {
id = 'profiles' id = 'profiles'
icon = 'window-restore' icon = 'window-restore'
title = 'Profiles & connections' title = this.translate.instant('Profiles & connections')
prioritized = true prioritized = true
constructor (private translate: TranslateService) { super() }
getComponentType (): any { getComponentType (): any {
return ProfilesSettingsTabComponent return ProfilesSettingsTabComponent
} }
@ -63,7 +70,9 @@ export class ProfilesSettingsTabProvider extends SettingsTabProvider {
export class ConfigSyncSettingsTabProvider extends SettingsTabProvider { export class ConfigSyncSettingsTabProvider extends SettingsTabProvider {
id = 'config-sync' id = 'config-sync'
icon = 'cloud' icon = 'cloud'
title = 'Config sync' title = this.translate.instant('Config sync')
constructor (private translate: TranslateService) { super() }
getComponentType (): any { getComponentType (): any {
return ConfigSyncSettingsTabComponent return ConfigSyncSettingsTabComponent

View File

@ -1,5 +1,5 @@
.d-flex .d-flex
strong Keyboard-interactive auth strong(translate) Keyboard-interactive auth
.ml-2 {{prompt.name}} .ml-2 {{prompt.name}}
.prompt-text {{prompt.prompts[step].prompt}} .prompt-text {{prompt.prompts[step].prompt}}

View File

@ -1,6 +1,6 @@
.modal-body .modal-body
label Deleting label(translate) Deleting
.no-wrap {{progressMessage}} .no-wrap {{progressMessage}}
.modal-footer .modal-footer
button.btn.btn-danger((click)='cancel()') Cancel button.btn.btn-danger((click)='cancel()', translate) Cancel

View File

@ -8,21 +8,21 @@
button.btn.btn-link.btn-sm.d-flex((click)='upload()') button.btn.btn-link.btn-sm.d-flex((click)='upload()')
i.fas.fa-upload.mr-1 i.fas.fa-upload.mr-1
div Upload div(translate) Upload
button.btn.btn-link.btn-close((click)='close()') !{require('../../../tabby-core/src/icons/times.svg')} button.btn.btn-link.btn-close((click)='close()') !{require('../../../tabby-core/src/icons/times.svg')}
.body(dropZone, (transfer)='uploadOne($event)') .body(dropZone, (transfer)='uploadOne($event)')
div(*ngIf='!sftp') Connecting div(*ngIf='!sftp', translate) Connecting
div(*ngIf='sftp') div(*ngIf='sftp')
div(*ngIf='fileList === null') Loading div(*ngIf='fileList === null', translate) Loading
.list-group.list-group-light(*ngIf='fileList !== null') .list-group.list-group-light(*ngIf='fileList !== null')
.list-group-item.list-group-item-action.d-flex.align-items-center( .list-group-item.list-group-item-action.d-flex.align-items-center(
*ngIf='path !== "/"', *ngIf='path !== "/"',
(click)='goUp()' (click)='goUp()'
) )
i.fas.fa-fw.fa-level-up-alt i.fas.fa-fw.fa-level-up-alt
div Go up div(translate) Go up
.list-group-item.list-group-item-action.d-flex.align-items-center( .list-group-item.list-group-item-action.d-flex.align-items-center(
*ngFor='let item of fileList', *ngFor='let item of fileList',
(contextmenu)='showContextMenu(item, $event)', (contextmenu)='showContextMenu(item, $event)',

Some files were not shown because too many files have changed in this diff Show More