feat(i18n): translate the interface and the desktop file

main
reionwong 1 week ago
parent 7a3c97bedd
commit d469ea99cb

@ -28,7 +28,9 @@ target_include_directories(${PROJECT_NAME} PRIVATE src)
target_link_libraries(${PROJECT_NAME} PRIVATE terminal-view)
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES cutefish-terminal.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
cutefish_add_translations(cutefish-terminal SOURCES src DESKTOP_FILES cutefish-terminal.desktop)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cutefish-terminal.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
if(BUILD_TESTING)
add_subdirectory(tests)

@ -1,7 +1,6 @@
[Desktop Entry]
Version=1.0
Name=Terminal
Name[zh_CN]=终端
Exec=/usr/bin/cutefish-terminal
Icon=utilities-terminal
Terminal=false

@ -0,0 +1,211 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: cutefish-terminal\n"
"Report-Msgid-Bugs-To: https://github.com/cutefishos\n"
"POT-Creation-Date: 2026-10-19 22:34+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: src/findbar.cpp
msgid "Find"
msgstr ""
#: src/settingsdialog.cpp
msgid "Theme"
msgstr ""
#: src/settingsdialog.cpp
msgid "Font"
msgstr ""
#: src/settingsdialog.cpp
msgid "Font Size"
msgstr ""
#: src/settingsdialog.cpp
msgid "Blinking Cursor"
msgstr ""
#: src/settingsdialog.cpp
msgid "Opacity"
msgstr ""
#: src/settingsdialog.cpp
msgid "Window Blur"
msgstr ""
#: src/settingsdialog.cpp
msgid "Cursor Shape"
msgstr ""
#. TRANSLATORS: the shapes of the text cursor.
#: src/settingsdialog.cpp
msgid "Block"
msgstr ""
#: src/settingsdialog.cpp
msgid "Underline"
msgstr ""
#: src/settingsdialog.cpp
msgid "I-Beam"
msgstr ""
#: src/settingsdialog.cpp
msgid "Scrollback"
msgstr ""
#: src/settingsdialog.cpp
msgid "1,000 lines"
msgstr ""
#: src/settingsdialog.cpp
msgid "10,000 lines"
msgstr ""
#: src/settingsdialog.cpp
msgid "100,000 lines"
msgstr ""
#: src/settingsdialog.cpp
msgid "Unlimited"
msgstr ""
#: src/settingsdialog.cpp
msgid "Flash on Bell"
msgstr ""
#: src/settingsdialog.cpp
msgid "Warn Before Pasting Multiple Lines"
msgstr ""
#. TRANSLATORS: a font size in points.
#: src/settingsdialog.cpp src/terminalwindow.cpp
#, python-brace-format
msgid "{size} pt"
msgstr ""
#. TRANSLATORS: a percentage, such as "80%".
#: src/settingsdialog.cpp
#, python-brace-format
msgid "{percent}%"
msgstr ""
#: src/settingsdialog.cpp src/terminalpage.cpp
msgid "Settings"
msgstr ""
#: src/terminalpage.cpp
msgid "Open Link"
msgstr ""
#: src/terminalpage.cpp
msgid "Copy Link"
msgstr ""
#: src/terminalpage.cpp
#, python-brace-format
msgid "Open “{name}”"
msgstr ""
#: src/terminalpage.cpp
msgid "Show in File Manager"
msgstr ""
#: src/terminalpage.cpp
msgid "Copy"
msgstr ""
#: src/terminalpage.cpp src/terminalwindow.cpp
msgid "Paste"
msgstr ""
#: src/terminalpage.cpp
msgid "Select All"
msgstr ""
#: src/terminalpage.cpp
msgid "Find…"
msgstr ""
#: src/terminalpage.cpp
msgid "Clear Scrollback"
msgstr ""
#: src/terminalpage.cpp
msgid "Reset Terminal"
msgstr ""
#: src/terminalpage.cpp
msgid "New Tab"
msgstr ""
#: src/terminalpage.cpp
msgid "New Window"
msgstr ""
#: src/terminalpage.cpp
msgid "Open File Manager"
msgstr ""
#: src/terminalpage.cpp
msgid "Exit full screen"
msgstr ""
#: src/terminalpage.cpp
msgid "Full screen"
msgstr ""
#: src/terminalwindow.cpp
msgid "Rename Tab…"
msgstr ""
#: src/terminalwindow.cpp
msgid "Close Tab"
msgstr ""
#: src/terminalwindow.cpp
msgid "Close Other Tabs"
msgstr ""
#: src/terminalwindow.cpp
msgid "Process is running, are you sure you want to quit?"
msgstr ""
#: src/terminalwindow.cpp
msgid "Cancel"
msgstr ""
#: src/terminalwindow.cpp
msgid "OK"
msgstr ""
#: src/terminalwindow.cpp
msgid "Open this link?"
msgstr ""
#: src/terminalwindow.cpp
msgid "Open"
msgstr ""
#: src/terminalwindow.cpp
#, python-brace-format
msgid "Paste {n} line?"
msgid_plural "Paste {n} lines?"
msgstr[0] ""
msgstr[1] ""
#: src/terminalwindow.cpp
msgid "Paste this line and press Enter?"
msgstr ""
#: cutefish-terminal.desktop
msgid "Terminal"
msgstr ""

@ -5,6 +5,7 @@
#include <algorithm>
#include "base/i18n.h"
#include "base/strings.h"
#include "ui/header_button.h"
#include "ui/keys.h"
@ -16,6 +17,7 @@
#include "TerminalView.h"
using namespace cutefish::ui;
using cutefish::i18n::tr;
// The field's keys: Enter searches, Shift+Enter searches the other way, Escape closes.
class FindField : public TextField
@ -58,7 +60,7 @@ FindBar::FindBar(Konsole::TerminalView* terminal)
setVisible(false);
m_field->setFrameVisible(false);
m_field->setPlaceholder("Find");
m_field->setPlaceholder(tr("Find"));
m_field->setTextMargins(0, 0);
m_previous->setGlyph("chevron-up", 16);
m_next->setGlyph("chevron-down", 16);

@ -10,6 +10,7 @@
#include <unistd.h>
#include <vector>
#include "base/i18n.h"
#include "ui/application.h"
#include "terminalsettings.h"
@ -96,6 +97,7 @@ int main(int argc, char** argv)
ui::Application app(argc, argv);
app.setApplicationId("cutefish-terminal");
i18n::setDomain("cutefish-terminal");
// Character widths follow the locale.
std::setlocale(LC_ALL, "");

@ -8,6 +8,7 @@
#include <map>
#include "base/event_loop.h"
#include "base/i18n.h"
#include "ui/combo_box.h"
#include "ui/dialog_window.h"
#include "ui/label.h"
@ -25,6 +26,8 @@
#include "terminalwindow.h"
using namespace cutefish::ui;
using cutefish::i18n::format;
using cutefish::i18n::tr;
// Bundled schemes, by file name, and their labels.
static const std::vector<std::string> kThemeNames = {
@ -269,11 +272,11 @@ public:
TerminalSettings& settings = window->settings();
Card* appearance = addCard();
theme = appearance->addRow("Theme")->addControl<ComboBox>(kThemeLabels);
theme = appearance->addRow(tr("Theme"))->addControl<ComboBox>(kThemeLabels);
setWidth(theme, 200);
theme->onActivated = [&settings](int index) { settings.setColorScheme(kThemeNames[size_t(index)]); };
SettingRow* fontRow = appearance->addRow("Font");
SettingRow* fontRow = appearance->addRow(tr("Font"));
font = fontRow->addControl<ComboBox>();
setWidth(font, 200);
font->onActivated = [this, &settings](int index) {
@ -281,7 +284,7 @@ public:
settings.setFontName(m_families[size_t(index)]);
};
SettingRow* sizeRow = appearance->addRow("Font Size");
SettingRow* sizeRow = appearance->addRow(tr("Font Size"));
fontSize = sizeRow->addControl<Slider>();
sizeRow->setControlWidth(fontSize, 160);
fontSize->setRange(5, 30);
@ -289,11 +292,11 @@ public:
fontSize->onMoved = [&settings](double value) { settings.setFontPointSize(int(std::lround(value))); };
fontSizeValue = addValueLabel(sizeRow);
blinking = appearance->addRow("Blinking Cursor", true)->addControl<Switch>();
blinking = appearance->addRow(tr("Blinking Cursor"), true)->addControl<Switch>();
blinking->onToggled = [&settings](bool checked) { settings.setBlinkingCursor(checked); };
Card* window2 = addCard();
SettingRow* opacityRow = window2->addRow("Opacity");
SettingRow* opacityRow = window2->addRow(tr("Opacity"));
opacity = opacityRow->addControl<Slider>();
opacityRow->setControlWidth(opacity, 160);
opacity->setRange(0.1, 1.0);
@ -301,24 +304,25 @@ public:
opacity->onMoved = [&settings](double value) { settings.setOpacity(value); };
opacityValue = addValueLabel(opacityRow);
blur = window2->addRow("Window Blur", true)->addControl<Switch>();
blur = window2->addRow(tr("Window Blur"), true)->addControl<Switch>();
blur->onToggled = [&settings](bool checked) { settings.setBlur(checked); };
Card* behaviour = addCard();
// In the order of the cursorShape values.
cursorShape = behaviour->addRow("Cursor Shape")->addControl<ComboBox>(std::vector<std::string>{
"Block", "Underline", "I-Beam"});
cursorShape = behaviour->addRow(tr("Cursor Shape"))->addControl<ComboBox>(std::vector<std::string>{
// TRANSLATORS: the shapes of the text cursor.
tr("Block"), tr("Underline"), tr("I-Beam")});
setWidth(cursorShape, 160);
cursorShape->onActivated = [&settings](int index) { settings.setCursorShape(index); };
scrollback = behaviour->addRow("Scrollback")->addControl<ComboBox>(std::vector<std::string>{
"1,000 lines", "10,000 lines", "100,000 lines", "Unlimited"});
scrollback = behaviour->addRow(tr("Scrollback"))->addControl<ComboBox>(std::vector<std::string>{
tr("1,000 lines"), tr("10,000 lines"), tr("100,000 lines"), tr("Unlimited")});
setWidth(scrollback, 160);
scrollback->onActivated = [&settings](int index) { settings.setScrollbackLines(kScrollbackLines[size_t(index)]); };
visualBell = behaviour->addRow("Flash on Bell")->addControl<Switch>();
visualBell = behaviour->addRow(tr("Flash on Bell"))->addControl<Switch>();
visualBell->onToggled = [&settings](bool checked) { settings.setVisualBell(checked); };
confirmPaste = behaviour->addRow("Warn Before Pasting Multiple Lines", true)->addControl<Switch>();
confirmPaste = behaviour->addRow(tr("Warn Before Pasting Multiple Lines"), true)->addControl<Switch>();
confirmPaste->onToggled = [&settings](bool checked) { settings.setConfirmMultilinePaste(checked); };
}
@ -341,10 +345,13 @@ public:
font->setCurrentIndex(family == m_families.end() ? 0 : int(family - m_families.begin()));
fontSize->setValue(settings.fontPointSize());
fontSizeValue->setText(std::to_string(settings.fontPointSize()) + " pt");
// TRANSLATORS: a font size in points.
fontSizeValue->setText(format(tr("{size} pt"), {{"size", std::to_string(settings.fontPointSize())}}));
blinking->setChecked(settings.blinkingCursor());
opacity->setValue(settings.opacity());
opacityValue->setText(std::to_string(int(std::lround(settings.opacity() * 100))) + "%");
// TRANSLATORS: a percentage, such as "80%".
opacityValue->setText(format(tr("{percent}%"),
{{"percent", std::to_string(int(std::lround(settings.opacity() * 100)))}}));
blur->setChecked(settings.blur());
cursorShape->setCurrentIndex(settings.cursorShape());
const auto lines = std::find(kScrollbackLines.begin(), kScrollbackLines.end(), settings.scrollbackLines());
@ -434,7 +441,7 @@ void SettingsDialog::update()
void SettingsDialog::show()
{
if (!m_dialog) {
m_dialog = std::make_unique<DialogWindow>(&m_window->window(), "Settings", kDialogWidth);
m_dialog = std::make_unique<DialogWindow>(&m_window->window(), tr("Settings"), kDialogWidth);
m_content = m_dialog->setContent<SettingsContent>(m_window);
m_dialog->onClosed = [this] {
m_content = nullptr;

@ -6,6 +6,7 @@
#include <algorithm>
#include <cmath>
#include "base/i18n.h"
#include "base/strings.h"
#include "base/url.h"
#include "ui/clipboard.h"
@ -26,6 +27,8 @@
#include "terminalwindow.h"
using namespace cutefish::ui;
using cutefish::i18n::format;
using cutefish::i18n::tr;
namespace strings = cutefish::base::strings;
// The terminal's margins inside the page; the top is the header's line.
@ -400,34 +403,34 @@ void TerminalPage::openMenu(double x, double y)
m_menu->clear();
if (!m_menuLink.empty()) {
m_menu->addItem("Open Link", [this] { m_window->openLink(m_menuLink); });
m_menu->addItem("Copy Link", [this] { Clipboard::setText(m_menuLink); });
m_menu->addItem(tr("Open Link"), [this] { m_window->openLink(m_menuLink); });
m_menu->addItem(tr("Copy Link"), [this] { Clipboard::setText(m_menuLink); });
m_menu->addSeparator();
}
if (!m_menuPath.empty()) {
const std::string name = m_menuPath.substr(m_menuPath.rfind('/') + 1);
m_menu->addItem("Open \xe2\x80\x9c" + elided(name.empty() ? m_menuPath : name, 30) + "\xe2\x80\x9d",
m_menu->addItem(format(tr("Open “{name}”"), {{"name", elided(name.empty() ? m_menuPath : name, 30)}}),
[this] { ProcessHelper::openUrl(m_menuPath); });
m_menu->addItem("Show in File Manager", [this] { ProcessHelper::showInFileManager(m_menuPath); });
m_menu->addItem(tr("Show in File Manager"), [this] { ProcessHelper::showInFileManager(m_menuPath); });
m_menu->addSeparator();
}
if (m_terminal->hasSelection())
m_menu->addItem("Copy", [this] { m_terminal->copyClipboard(); });
m_menu->addItem(tr("Copy"), [this] { m_terminal->copyClipboard(); });
if (m_menuCanPaste)
m_menu->addItem("Paste", [this] { m_terminal->pasteClipboard(); });
m_menu->addItem("Select All", [this] { m_terminal->selectAll(); });
m_menu->addItem("Find\xe2\x80\xa6", [this] { openFind(); });
m_menu->addItem(tr("Paste"), [this] { m_terminal->pasteClipboard(); });
m_menu->addItem(tr("Select All"), [this] { m_terminal->selectAll(); });
m_menu->addItem(tr("Find…"), [this] { openFind(); });
if (m_terminal->scrollbarMaximum() > 0)
m_menu->addItem("Clear Scrollback", [this] { m_session->clearScrollback(); });
m_menu->addItem("Reset Terminal", [this] { m_session->resetTerminal(); });
m_menu->addItem(tr("Clear Scrollback"), [this] { m_session->clearScrollback(); });
m_menu->addItem(tr("Reset Terminal"), [this] { m_session->resetTerminal(); });
m_menu->addSeparator();
m_menu->addItem("New Tab", [this] { m_window->openNewTab(); });
m_menu->addItem("New Window", [this] { m_window->newWindow(); });
m_menu->addItem("Open File Manager", [this] { ProcessHelper::openFileManager(m_session->currentDir()); });
m_menu->addItem(tr("New Tab"), [this] { m_window->openNewTab(); });
m_menu->addItem(tr("New Window"), [this] { m_window->newWindow(); });
m_menu->addItem(tr("Open File Manager"), [this] { ProcessHelper::openFileManager(m_session->currentDir()); });
m_menu->addSeparator();
m_menu->addItem(m_window->isFullScreen() ? "Exit full screen" : "Full screen",
m_menu->addItem(m_window->isFullScreen() ? tr("Exit full screen") : tr("Full screen"),
[this] { m_window->toggleFullScreen(); });
m_menu->addItem("Settings", [this] { m_window->showSettings(); });
m_menu->addItem(tr("Settings"), [this] { m_window->showSettings(); });
if (Window* window = this->window())
m_menu->popup(window, position);

@ -7,6 +7,7 @@
#include <cmath>
#include "base/event_loop.h"
#include "base/i18n.h"
#include "base/strings.h"
#include "ui/animation.h"
#include "ui/dialog_window.h"
@ -32,6 +33,9 @@
#include "windows.h"
using namespace cutefish::ui;
using cutefish::i18n::format;
using cutefish::i18n::tr;
using cutefish::i18n::trn;
namespace strings = cutefish::base::strings;
static constexpr double kHeaderHeight = 40;
@ -205,7 +209,7 @@ public:
// Shows the size briefly while zooming.
void showZoom(int pointSize)
{
m_zoomLabel->setText(std::to_string(pointSize) + " pt");
m_zoomLabel->setText(format(tr("{size} pt"), {{"size", std::to_string(pointSize)}}));
if (m_zoomFade)
m_zoomFade->stop();
m_zoomOpacity = 1;
@ -602,11 +606,11 @@ void TerminalWindow::openTabMenu(int index, PointF position)
if (!m_tabMenu)
m_tabMenu = std::make_unique<Menu>();
m_tabMenu->clear();
m_tabMenu->addItem("Rename Tab\xe2\x80\xa6", [this] { m_headerContent->tabBar->startRename(m_tabMenuIndex); });
m_tabMenu->addItem(tr("Rename Tab…"), [this] { m_headerContent->tabBar->startRename(m_tabMenuIndex); });
m_tabMenu->addSeparator();
m_tabMenu->addItem("Close Tab", [this] { closeProtection(m_tabMenuIndex); });
m_tabMenu->addItem(tr("Close Tab"), [this] { closeProtection(m_tabMenuIndex); });
if (tabCount() > 1)
m_tabMenu->addItem("Close Other Tabs", [this] { closeOtherTabs(m_tabMenuIndex); });
m_tabMenu->addItem(tr("Close Other Tabs"), [this] { closeOtherTabs(m_tabMenuIndex); });
m_tabMenu->popup(m_window.get(), position);
}
@ -634,9 +638,9 @@ void TerminalWindow::confirmClose(const std::vector<TerminalPage*>& pages, std::
}
if (!m_exitPrompt) {
m_exitPrompt = std::make_unique<ConfirmDialog>(m_window.get(), "Process is running, are you sure you want to quit?");
m_exitPrompt->setCancelText("Cancel");
m_exitPrompt->setConfirmText("OK");
m_exitPrompt = std::make_unique<ConfirmDialog>(m_window.get(), tr("Process is running, are you sure you want to quit?"));
m_exitPrompt->setCancelText(tr("Cancel"));
m_exitPrompt->setConfirmText(tr("OK"));
m_exitPrompt->setDestructive(true);
m_exitPrompt->onAccepted = [this] {
if (auto pending = std::exchange(m_pendingAction, nullptr))
@ -782,8 +786,8 @@ void TerminalWindow::openLink(const std::string& url)
}
if (!m_linkConfirm) {
m_linkConfirm = std::make_unique<ConfirmDialog>(m_window.get(), "Open this link?");
m_linkConfirm->setConfirmText("Open");
m_linkConfirm = std::make_unique<ConfirmDialog>(m_window.get(), tr("Open this link?"));
m_linkConfirm->setConfirmText(tr("Open"));
m_linkConfirm->onAccepted = [this] { ProcessHelper::openUrl(m_pendingLink); };
}
m_pendingLink = url;
@ -810,7 +814,7 @@ void TerminalWindow::confirmPaste(const std::string& text, std::function<void()>
if (!m_pasteConfirm) {
m_pasteConfirm = std::make_unique<ConfirmDialog>(m_window.get(), std::string());
m_pasteConfirm->setConfirmText("Paste");
m_pasteConfirm->setConfirmText(tr("Paste"));
m_pasteConfirm->onAccepted = [this] {
if (auto pending = std::exchange(m_pendingPaste, nullptr))
pending();
@ -819,8 +823,8 @@ void TerminalWindow::confirmPaste(const std::string& text, std::function<void()>
}
const size_t count = lines.size();
m_pasteConfirm->setTitle(count > 1 ? "Paste " + std::to_string(count) + " lines?"
: "Paste this line and press Enter?");
m_pasteConfirm->setTitle(count > 1 ? trn("Paste {n} line?", "Paste {n} lines?", count)
: std::string(tr("Paste this line and press Enter?")));
m_pasteConfirm->setText(strings::join(shown, "\n"));
m_pendingPaste = std::move(action);
m_pasteConfirm->show();

Loading…
Cancel
Save