@@ -59,7 +59,7 @@ int main(int argc, char* argv[]) { | |||||
// Handle will be closed by Windows when the process ends | // Handle will be closed by Windows when the process ends | ||||
HANDLE instanceMutex = CreateMutexW(NULL, true, string::UTF8toUTF16(APP_NAME).c_str()); | HANDLE instanceMutex = CreateMutexW(NULL, true, string::UTF8toUTF16(APP_NAME).c_str()); | ||||
if (GetLastError() == ERROR_ALREADY_EXISTS) { | if (GetLastError() == ERROR_ALREADY_EXISTS) { | ||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "VCV Rack is already running. Multiple Rack instances are not supported."); | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, string::translate("standalone.multipleInstances")); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
(void) instanceMutex; | (void) instanceMutex; | ||||
@@ -174,7 +174,8 @@ int main(int argc, char* argv[]) { | |||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
std::string msg = e.what(); | std::string msg = e.what(); | ||||
msg += "\n\nReset settings to default?"; | |||||
msg += "\n\n"; | |||||
msg += string::translate("standalone.resetSettings"); | |||||
if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, msg.c_str())) { | if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, msg.c_str())) { | ||||
exit(1); | exit(1); | ||||
} | } | ||||
@@ -184,7 +185,7 @@ int main(int argc, char* argv[]) { | |||||
// Check existence of the system res/ directory | // Check existence of the system res/ directory | ||||
std::string resDir = asset::system("res"); | std::string resDir = asset::system("res"); | ||||
if (!system::isDirectory(resDir)) { | if (!system::isDirectory(resDir)) { | ||||
std::string message = string::f("VCV Rack's resource directory \"%s\" does not exist. Make sure Rack is correctly installed and launched.", resDir.c_str()); | |||||
std::string message = string::f(string::translate("standalone.resDir"), resDir); | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, message.c_str()); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
@@ -196,8 +197,7 @@ int main(int argc, char* argv[]) { | |||||
rtaudioInit(); | rtaudioInit(); | ||||
#if defined ARCH_MAC | #if defined ARCH_MAC | ||||
if (rtaudioIsMicrophoneBlocked()) { | if (rtaudioIsMicrophoneBlocked()) { | ||||
std::string msg = "VCV Rack cannot access audio input because Microphone permission is blocked."; | |||||
msg += "\n\nGive permission to Rack by opening Apple's System Settings and enabling Privacy & Security > Microphone > " + APP_NAME + " " + APP_VERSION_MAJOR + " " + APP_EDITION_NAME + "."; | |||||
std::string msg = string::f(string::translate("standalone.micPermission"), APP_NAME + " " + APP_VERSION_MAJOR + " " + APP_EDITION_NAME); | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, msg.c_str()); | osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, msg.c_str()); | ||||
} | } | ||||
#endif | #endif | ||||
@@ -252,7 +252,7 @@ int main(int argc, char* argv[]) { | |||||
#endif | #endif | ||||
// Initialize patch | // Initialize patch | ||||
if (logger::wasTruncated() && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "VCV Rack crashed during the last session, possibly due to a buggy module in your patch. Clear your patch and start over?")) { | |||||
if (logger::wasTruncated() && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, string::translate("standalone.crashed").c_str())) { | |||||
// Do nothing, which leaves a blank patch | // Do nothing, which leaves a blank patch | ||||
} | } | ||||
else { | else { | ||||
@@ -49,14 +49,14 @@ static void appendAudioDriverMenu(ui::Menu* menu, audio::Port* port) { | |||||
void AudioDriverChoice::onAction(const ActionEvent& e) { | void AudioDriverChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("Audio driver")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.audioDriver"))); | |||||
appendAudioDriverMenu(menu, port); | appendAudioDriverMenu(menu, port); | ||||
} | } | ||||
void AudioDriverChoice::step() { | void AudioDriverChoice::step() { | ||||
text = ""; | text = ""; | ||||
if (box.size.x >= 200.0) | if (box.size.x >= 200.0) | ||||
text += "Driver: "; | |||||
text += string::translate("AudioDisplay.driver"); | |||||
audio::Driver* driver = port ? port->getDriver() : NULL; | audio::Driver* driver = port ? port->getDriver() : NULL; | ||||
std::string driverName = driver ? driver->getName() : ""; | std::string driverName = driver ? driver->getName() : ""; | ||||
if (driverName != "") { | if (driverName != "") { | ||||
@@ -64,7 +64,7 @@ void AudioDriverChoice::step() { | |||||
color.a = 1.0; | color.a = 1.0; | ||||
} | } | ||||
else { | else { | ||||
text += "(No driver)"; | |||||
text += "(" + string::translate("AudioDisplay.noDriver") + ")"; | |||||
color.a = 0.5; | color.a = 0.5; | ||||
} | } | ||||
} | } | ||||
@@ -99,7 +99,7 @@ static void appendAudioDeviceMenu(ui::Menu* menu, audio::Port* port) { | |||||
AudioDeviceValueItem* item = new AudioDeviceValueItem; | AudioDeviceValueItem* item = new AudioDeviceValueItem; | ||||
item->port = port; | item->port = port; | ||||
item->deviceId = -1; | item->deviceId = -1; | ||||
item->text = "(No device)"; | |||||
item->text = "(" + string::translate("AudioDisplay.noDevice") + ")"; | |||||
item->rightText = CHECKMARK(item->deviceId == port->getDeviceId()); | item->rightText = CHECKMARK(item->deviceId == port->getDeviceId()); | ||||
menu->addChild(item); | menu->addChild(item); | ||||
} | } | ||||
@@ -132,14 +132,14 @@ static void appendAudioDeviceMenu(ui::Menu* menu, audio::Port* port) { | |||||
void AudioDeviceChoice::onAction(const ActionEvent& e) { | void AudioDeviceChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("Audio device")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.audioDevice"))); | |||||
appendAudioDeviceMenu(menu, port); | appendAudioDeviceMenu(menu, port); | ||||
} | } | ||||
void AudioDeviceChoice::step() { | void AudioDeviceChoice::step() { | ||||
text = ""; | text = ""; | ||||
if (box.size.x >= 200.0) | if (box.size.x >= 200.0) | ||||
text += "Device: "; | |||||
text += string::translate("AudioDisplay.device"); | |||||
std::string detail = ""; | std::string detail = ""; | ||||
if (port && port->getDevice()) | if (port && port->getDevice()) | ||||
detail = getDetailTemplate(port->getDevice()->getName(), port->getNumInputs(), port->inputOffset, port->getNumOutputs(), port->outputOffset); | detail = getDetailTemplate(port->getDevice()->getName(), port->getNumInputs(), port->inputOffset, port->getNumOutputs(), port->outputOffset); | ||||
@@ -149,10 +149,7 @@ void AudioDeviceChoice::step() { | |||||
color.a = 1.0; | color.a = 1.0; | ||||
} | } | ||||
else { | else { | ||||
if (box.size.x >= 80.0) | |||||
text += "(No device)"; | |||||
else | |||||
text += "No device"; | |||||
text += string::translate("AudioDisplay.noDevice"); | |||||
color.a = 0.5; | color.a = 0.5; | ||||
} | } | ||||
} | } | ||||
@@ -184,7 +181,7 @@ static void appendAudioSampleRateMenu(ui::Menu* menu, audio::Port* port) { | |||||
sampleRates.insert(port->getSampleRate()); | sampleRates.insert(port->getSampleRate()); | ||||
if (sampleRates.empty()) { | if (sampleRates.empty()) { | ||||
menu->addChild(createMenuLabel("(Locked by device)")); | |||||
menu->addChild(createMenuLabel("(" + string::translate("AudioDisplay.lockedByDevice") + ")")); | |||||
} | } | ||||
for (float sampleRate : sampleRates) { | for (float sampleRate : sampleRates) { | ||||
if (sampleRate <= 0) | if (sampleRate <= 0) | ||||
@@ -200,14 +197,14 @@ static void appendAudioSampleRateMenu(ui::Menu* menu, audio::Port* port) { | |||||
void AudioSampleRateChoice::onAction(const ActionEvent& e) { | void AudioSampleRateChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("Sample rate")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.sampleRate"))); | |||||
appendAudioSampleRateMenu(menu, port); | appendAudioSampleRateMenu(menu, port); | ||||
} | } | ||||
void AudioSampleRateChoice::step() { | void AudioSampleRateChoice::step() { | ||||
text = ""; | text = ""; | ||||
if (box.size.x >= 100.0) | if (box.size.x >= 100.0) | ||||
text += "Rate: "; | |||||
text += string::translate("AudioDisplay.sampleRateColon"); | |||||
float sampleRate = port ? port->getSampleRate() : 0; | float sampleRate = port ? port->getSampleRate() : 0; | ||||
if (sampleRate > 0) { | if (sampleRate > 0) { | ||||
text += string::f("%g", sampleRate / 1000.f); | text += string::f("%g", sampleRate / 1000.f); | ||||
@@ -247,7 +244,7 @@ static void appendAudioBlockSizeMenu(ui::Menu* menu, audio::Port* port) { | |||||
blockSizes.insert(port->getBlockSize()); | blockSizes.insert(port->getBlockSize()); | ||||
if (blockSizes.empty()) { | if (blockSizes.empty()) { | ||||
menu->addChild(createMenuLabel("(Locked by device)")); | |||||
menu->addChild(createMenuLabel("(" + string::translate("AudioDisplay.lockedByDevice") + ")")); | |||||
} | } | ||||
for (int blockSize : blockSizes) { | for (int blockSize : blockSizes) { | ||||
if (blockSize <= 0) | if (blockSize <= 0) | ||||
@@ -264,14 +261,14 @@ static void appendAudioBlockSizeMenu(ui::Menu* menu, audio::Port* port) { | |||||
void AudioBlockSizeChoice::onAction(const ActionEvent& e) { | void AudioBlockSizeChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("Block size")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.blockSize"))); | |||||
appendAudioBlockSizeMenu(menu, port); | appendAudioBlockSizeMenu(menu, port); | ||||
} | } | ||||
void AudioBlockSizeChoice::step() { | void AudioBlockSizeChoice::step() { | ||||
text = ""; | text = ""; | ||||
if (box.size.x >= 100.0) | if (box.size.x >= 100.0) | ||||
text += "Block size: "; | |||||
text += string::translate("AudioDisplay.blockSizeColon"); | |||||
int blockSize = port ? port->getBlockSize() : 0; | int blockSize = port ? port->getBlockSize() : 0; | ||||
if (blockSize > 0) { | if (blockSize > 0) { | ||||
text += string::f("%d", blockSize); | text += string::f("%d", blockSize); | ||||
@@ -358,36 +355,36 @@ void AudioButton::onAction(const ActionEvent& e) { | |||||
void appendAudioMenu(ui::Menu* menu, audio::Port* port) { | void appendAudioMenu(ui::Menu* menu, audio::Port* port) { | ||||
menu->addChild(createMenuLabel("Audio driver")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.audioDriver"))); | |||||
appendAudioDriverMenu(menu, port); | appendAudioDriverMenu(menu, port); | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
menu->addChild(createMenuLabel("Audio device")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.audioDevice"))); | |||||
appendAudioDeviceMenu(menu, port); | appendAudioDeviceMenu(menu, port); | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
menu->addChild(createMenuLabel("Sample rate")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.sampleRate"))); | |||||
appendAudioSampleRateMenu(menu, port); | appendAudioSampleRateMenu(menu, port); | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
menu->addChild(createMenuLabel("Block size")); | |||||
menu->addChild(createMenuLabel(string::translate("AudioDisplay.blockSize"))); | |||||
appendAudioBlockSizeMenu(menu, port); | appendAudioBlockSizeMenu(menu, port); | ||||
// Uncomment this to use sub-menus instead of one big menu. | // Uncomment this to use sub-menus instead of one big menu. | ||||
// AudioDriverItem* driverItem = createMenuItem<AudioDriverItem>("Audio driver", RIGHT_ARROW); | |||||
// AudioDriverItem* driverItem = createMenuItem<AudioDriverItem>(string::translate("AudioDisplay.audioDriver"), RIGHT_ARROW); | |||||
// driverItem->port = port; | // driverItem->port = port; | ||||
// menu->addChild(driverItem); | // menu->addChild(driverItem); | ||||
// AudioDeviceItem* deviceItem = createMenuItem<AudioDeviceItem>("Audio device", RIGHT_ARROW); | |||||
// AudioDeviceItem* deviceItem = createMenuItem<AudioDeviceItem>(string::translate("AudioDisplay.audioDevice"), RIGHT_ARROW); | |||||
// deviceItem->port = port; | // deviceItem->port = port; | ||||
// menu->addChild(deviceItem); | // menu->addChild(deviceItem); | ||||
// AudioSampleRateItem* sampleRateItem = createMenuItem<AudioSampleRateItem>("Sample rate", RIGHT_ARROW); | |||||
// AudioSampleRateItem* sampleRateItem = createMenuItem<AudioSampleRateItem>(string::translate("AudioDisplay.sampleRate"), RIGHT_ARROW); | |||||
// sampleRateItem->port = port; | // sampleRateItem->port = port; | ||||
// menu->addChild(sampleRateItem); | // menu->addChild(sampleRateItem); | ||||
// AudioBlockSizeItem* blockSizeItem = createMenuItem<AudioBlockSizeItem>("Block size", RIGHT_ARROW); | |||||
// AudioBlockSizeItem* blockSizeItem = createMenuItem<AudioBlockSizeItem>(string::translate("AudioDisplay.blockSize"), RIGHT_ARROW); | |||||
// blockSizeItem->port = port; | // blockSizeItem->port = port; | ||||
// menu->addChild(blockSizeItem); | // menu->addChild(blockSizeItem); | ||||
} | } | ||||
@@ -989,7 +989,7 @@ inline void TagButton::onAction(const ActionEvent& e) { | |||||
noneItem->browser = browser; | noneItem->browser = browser; | ||||
menu->addChild(noneItem); | menu->addChild(noneItem); | ||||
menu->addChild(createMenuLabel(widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("Browser.tagsSelectMultiple"))); | |||||
menu->addChild(createMenuLabel(widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("key.click") + string::translate("Browser.tagsSelectMultiple"))); | |||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
for (int tagId = 0; tagId < (int) tag::tagAliases.size(); tagId++) { | for (int tagId = 0; tagId < (int) tag::tagAliases.size(); tagId++) { | ||||
@@ -107,7 +107,7 @@ void Knob::onDragEnd(const DragEndEvent& e) { | |||||
if (!std::isnan(internal->oldValue) && internal->oldValue != newValue) { | if (!std::isnan(internal->oldValue) && internal->oldValue != newValue) { | ||||
// Push ParamChange history action | // Push ParamChange history action | ||||
history::ParamChange* h = new history::ParamChange; | history::ParamChange* h = new history::ParamChange; | ||||
h->name = "move knob"; | |||||
h->name = string::translate("Knob.history.move"); | |||||
h->moduleId = module->id; | h->moduleId = module->id; | ||||
h->paramId = paramId; | h->paramId = paramId; | ||||
h->oldValue = internal->oldValue; | h->oldValue = internal->oldValue; | ||||
@@ -302,7 +302,7 @@ void Knob::onLeave(const LeaveEvent& e) { | |||||
if (!std::isnan(internal->oldValue) && internal->oldValue != newValue) { | if (!std::isnan(internal->oldValue) && internal->oldValue != newValue) { | ||||
// Push ParamChange history action | // Push ParamChange history action | ||||
history::ParamChange* h = new history::ParamChange; | history::ParamChange* h = new history::ParamChange; | ||||
h->name = "move knob"; | |||||
h->name = string::translate("Knob.history.move"); | |||||
h->moduleId = module->id; | h->moduleId = module->id; | ||||
h->paramId = paramId; | h->paramId = paramId; | ||||
h->oldValue = internal->oldValue; | h->oldValue = internal->oldValue; | ||||
@@ -31,14 +31,14 @@ static void appendMidiDriverMenu(ui::Menu* menu, midi::Port* port) { | |||||
void MidiDriverChoice::onAction(const ActionEvent& e) { | void MidiDriverChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("MIDI driver")); | |||||
menu->addChild(createMenuLabel(string::translate("MidiDisplay.driver"))); | |||||
appendMidiDriverMenu(menu, port); | appendMidiDriverMenu(menu, port); | ||||
} | } | ||||
void MidiDriverChoice::step() { | void MidiDriverChoice::step() { | ||||
text = (port && port->driver) ? port->getDriver()->getName() : ""; | text = (port && port->driver) ? port->getDriver()->getName() : ""; | ||||
if (text.empty()) { | if (text.empty()) { | ||||
text = "(No driver)"; | |||||
text = "(" + string::translate("MidiDisplay.noDriver") + ")"; | |||||
color.a = 0.5f; | color.a = 0.5f; | ||||
} | } | ||||
else { | else { | ||||
@@ -72,7 +72,7 @@ static void appendMidiDeviceMenu(ui::Menu* menu, midi::Port* port) { | |||||
MidiDeviceValueItem* item = new MidiDeviceValueItem; | MidiDeviceValueItem* item = new MidiDeviceValueItem; | ||||
item->port = port; | item->port = port; | ||||
item->deviceId = -1; | item->deviceId = -1; | ||||
item->text = "(No device)"; | |||||
item->text = "(" + string::translate("MidiDisplay.noDevice") + ")"; | |||||
item->rightText = CHECKMARK(item->deviceId == port->getDeviceId()); | item->rightText = CHECKMARK(item->deviceId == port->getDeviceId()); | ||||
menu->addChild(item); | menu->addChild(item); | ||||
} | } | ||||
@@ -89,14 +89,14 @@ static void appendMidiDeviceMenu(ui::Menu* menu, midi::Port* port) { | |||||
void MidiDeviceChoice::onAction(const ActionEvent& e) { | void MidiDeviceChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("MIDI device")); | |||||
menu->addChild(createMenuLabel(string::translate("MidiDisplay.device"))); | |||||
appendMidiDeviceMenu(menu, port); | appendMidiDeviceMenu(menu, port); | ||||
} | } | ||||
void MidiDeviceChoice::step() { | void MidiDeviceChoice::step() { | ||||
text = (port && port->device) ? port->getDevice()->getName() : ""; | text = (port && port->device) ? port->getDevice()->getName() : ""; | ||||
if (text.empty()) { | if (text.empty()) { | ||||
text = "(No device)"; | |||||
text = "(" + string::translate("MidiDisplay.noDevice") + ")"; | |||||
color.a = 0.5f; | color.a = 0.5f; | ||||
} | } | ||||
else { | else { | ||||
@@ -138,12 +138,12 @@ static void appendMidiChannelMenu(ui::Menu* menu, midi::Port* port) { | |||||
void MidiChannelChoice::onAction(const ActionEvent& e) { | void MidiChannelChoice::onAction(const ActionEvent& e) { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
menu->addChild(createMenuLabel("MIDI channel")); | |||||
menu->addChild(createMenuLabel(string::translate("MidiDisplay.channel"))); | |||||
appendMidiChannelMenu(menu, port); | appendMidiChannelMenu(menu, port); | ||||
} | } | ||||
void MidiChannelChoice::step() { | void MidiChannelChoice::step() { | ||||
text = port ? port->getChannelName(port->getChannel()) : "Channel 1"; | |||||
text = port ? port->getChannelName(port->getChannel()) : string::translate("MidiDisplay.channel1"); | |||||
} | } | ||||
struct MidiChannelItem : ui::MenuItem { | struct MidiChannelItem : ui::MenuItem { | ||||
@@ -203,28 +203,28 @@ void MidiButton::onAction(const ActionEvent& e) { | |||||
void appendMidiMenu(ui::Menu* menu, midi::Port* port) { | void appendMidiMenu(ui::Menu* menu, midi::Port* port) { | ||||
menu->addChild(createMenuLabel("MIDI driver")); | |||||
menu->addChild(createMenuLabel(string::translate("MidiDisplay.driver"))); | |||||
appendMidiDriverMenu(menu, port); | appendMidiDriverMenu(menu, port); | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
menu->addChild(createMenuLabel("MIDI device")); | |||||
menu->addChild(createMenuLabel(string::translate("MidiDisplay.device"))); | |||||
appendMidiDeviceMenu(menu, port); | appendMidiDeviceMenu(menu, port); | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
// menu->addChild(createMenuLabel("MIDI channel")); | |||||
// menu->addChild(createMenuLabel(string::translate("MidiDisplay.channel"))); | |||||
// appendMidiChannelMenu(menu, port); | // appendMidiChannelMenu(menu, port); | ||||
// Uncomment this to use sub-menus instead of one big menu. | // Uncomment this to use sub-menus instead of one big menu. | ||||
// MidiDriverItem* driverItem = createMenuItem<MidiDriverItem>("MIDI driver", RIGHT_ARROW); | |||||
// MidiDriverItem* driverItem = createMenuItem<MidiDriverItem>(string::translate("MidiDisplay.driver"), RIGHT_ARROW); | |||||
// driverItem->port = port; | // driverItem->port = port; | ||||
// menu->addChild(driverItem); | // menu->addChild(driverItem); | ||||
// MidiDeviceItem* deviceItem = createMenuItem<MidiDeviceItem>("MIDI device", RIGHT_ARROW); | |||||
// MidiDeviceItem* deviceItem = createMenuItem<MidiDeviceItem>(string::translate("MidiDisplay.device"), RIGHT_ARROW); | |||||
// deviceItem->port = port; | // deviceItem->port = port; | ||||
// menu->addChild(deviceItem); | // menu->addChild(deviceItem); | ||||
MidiChannelItem* channelItem = createMenuItem<MidiChannelItem>("MIDI channel", RIGHT_ARROW); | |||||
MidiChannelItem* channelItem = createMenuItem<MidiChannelItem>(string::translate("MidiDisplay.channel"), RIGHT_ARROW); | |||||
channelItem->port = port; | channelItem->port = port; | ||||
menu->addChild(channelItem); | menu->addChild(channelItem); | ||||
} | } | ||||
@@ -22,8 +22,8 @@ struct LightTooltip : ui::Tooltip { | |||||
if (!lightInfo) | if (!lightInfo) | ||||
return; | return; | ||||
// Label | // Label | ||||
text = lightInfo->getName(); | |||||
text += " light"; | |||||
std::string name = lightInfo->getName(); | |||||
text = string::f(string::translate("ModuleLightWidget.light"), name); | |||||
// Description | // Description | ||||
std::string description = lightInfo->getDescription(); | std::string description = lightInfo->getDescription(); | ||||
if (description != "") { | if (description != "") { | ||||
@@ -274,14 +274,14 @@ void ParamWidget::createContextMenu() { | |||||
// Initialize | // Initialize | ||||
if (pq && pq->resetEnabled && pq->isBounded()) { | if (pq && pq->resetEnabled && pq->isBounded()) { | ||||
menu->addChild(createMenuItem(string::translate("ParamWidget.initialize"), switchQuantity ? "" : string::translate("ParamWidget.doubleClick"), [=]() { | |||||
menu->addChild(createMenuItem(string::translate("ParamWidget.initialize"), switchQuantity ? "" : string::translate("key.doubleClick"), [=]() { | |||||
this->resetAction(); | this->resetAction(); | ||||
})); | })); | ||||
} | } | ||||
// Fine | // Fine | ||||
if (!switchQuantity) { | if (!switchQuantity) { | ||||
menu->addChild(createMenuItem(string::translate("ParamWidget.fine"), widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("ParamWidget.fineDrag"), NULL, true)); | |||||
menu->addChild(createMenuItem(string::translate("ParamWidget.fine"), widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("key.drag"), NULL, true)); | |||||
} | } | ||||
// Unmap | // Unmap | ||||
@@ -286,7 +286,7 @@ void PortWidget::createContextMenu() { | |||||
std::vector<CableWidget*> cws = APP->scene->rack->getCompleteCablesOnPort(this); | std::vector<CableWidget*> cws = APP->scene->rack->getCompleteCablesOnPort(this); | ||||
CableWidget* topCw = cws.empty() ? NULL : cws.back(); | CableWidget* topCw = cws.empty() ? NULL : cws.back(); | ||||
menu->addChild(createMenuItem(string::translate("PortWidget.deleteTopCable"), widget::getKeyCommandName(0, RACK_MOD_SHIFT) + string::translate("PortWidget.click"), | |||||
menu->addChild(createMenuItem(string::translate("PortWidget.deleteTopCable"), widget::getKeyCommandName(0, RACK_MOD_SHIFT) + string::translate("key.click"), | |||||
[=]() { | [=]() { | ||||
if (!weakThis) | if (!weakThis) | ||||
return; | return; | ||||
@@ -296,7 +296,7 @@ void PortWidget::createContextMenu() { | |||||
)); | )); | ||||
{ | { | ||||
PortCloneCableItem* item = createMenuItem<PortCloneCableItem>(string::translate("PortWidget.cloneTopCable"), widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("PortWidget.drag")); | |||||
PortCloneCableItem* item = createMenuItem<PortCloneCableItem>(string::translate("PortWidget.cloneTopCable"), widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("key.drag")); | |||||
item->disabled = !topCw; | item->disabled = !topCw; | ||||
item->pw = this; | item->pw = this; | ||||
item->cw = topCw; | item->cw = topCw; | ||||
@@ -304,7 +304,7 @@ void PortWidget::createContextMenu() { | |||||
} | } | ||||
{ | { | ||||
PortCreateCableItem* item = createMenuItem<PortCreateCableItem>(string::translate("PortWidget.createCableTop"), widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("PortWidget.drag")); | |||||
PortCreateCableItem* item = createMenuItem<PortCreateCableItem>(string::translate("PortWidget.createCableTop"), widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("key.drag")); | |||||
item->pw = this; | item->pw = this; | ||||
menu->addChild(item); | menu->addChild(item); | ||||
} | } | ||||
@@ -326,7 +326,7 @@ void PortWidget::createContextMenu() { | |||||
if (!cws.empty()) { | if (!cws.empty()) { | ||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
menu->addChild(createMenuLabel(string::translate("PortWidget.clickDrag") + string::translate("PortWidget.grabCable"))); | |||||
menu->addChild(createMenuLabel(string::translate("key.clickDrag") + string::translate("PortWidget.grabCable"))); | |||||
// Cable items | // Cable items | ||||
for (auto it = cws.rbegin(); it != cws.rend(); it++) { | for (auto it = cws.rbegin(); it != cws.rend(); it++) { | ||||
@@ -103,7 +103,7 @@ void Switch::onDragStart(const DragStartEvent& e) { | |||||
if (oldValue != newValue) { | if (oldValue != newValue) { | ||||
// Push ParamChange history action | // Push ParamChange history action | ||||
history::ParamChange* h = new history::ParamChange; | history::ParamChange* h = new history::ParamChange; | ||||
h->name = "move switch"; | |||||
h->name = string::translate("Switch.history.move"); | |||||
h->moduleId = module->id; | h->moduleId = module->id; | ||||
h->paramId = paramId; | h->paramId = paramId; | ||||
h->oldValue = oldValue; | h->oldValue = oldValue; | ||||
@@ -31,23 +31,36 @@ struct TipInfo { | |||||
}; | }; | ||||
// Remember to use “smart quotes.” | |||||
static const std::vector<TipInfo> tipInfos = { | |||||
{"To add a module to your patch, right-click an empty rack space or press Enter. Then click and drag a module from the Module Browser into the desired rack space.\n\nTo select multiple modules, click and drag on empty rack space.", "", ""}, | |||||
{"To move around your patch, use the scroll bars, drag while holding the middle mouse button, " RACK_MOD_ALT_NAME "+click and drag, or hold the arrow keys. Arrow key movement speed can be adjusted by holding " RACK_MOD_CTRL_NAME ", " RACK_MOD_SHIFT_NAME ", or " RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME ".\n\nTo zoom in and out, drag the Zoom slider in the View menu, hold " RACK_MOD_CTRL_NAME " and scroll, or press " RACK_MOD_CTRL_NAME "+= and " RACK_MOD_CTRL_NAME "+minus.", "", ""}, | |||||
{"You can use Rack in fullscreen mode by selecting “View > Fullscreen“ or pressing F11.\n\nIn fullscreen mode, the menu bar and scroll bars are hidden. This is ideal for screen recording with VCV Recorder.", "Get VCV Recorder", "https://vcvrack.com/Recorder"}, | |||||
{"You can browse thousands of modules on the VCV Library website.\n\nRegister for a VCV account, log into Rack using the Library menu, and browse the VCV Library to add or purchase modules. Keep all plugins up to date by clicking “Library > Update all”.", "VCV Library", "https://library.vcvrack.com/"}, | |||||
{"Some developers of free plugins accept donations. Right-click your favorite module's panel and select “Info > Donate”.\n\nYou can also donate via the module's VCV Library page.", "VCV Library", "https://library.vcvrack.com/"}, | |||||
{"Want to use VCV Rack in your DAW? VCV Rack Pro is available for VST2, VST3, Audio Unit, and CLAP hosts.\n\nSupported DAWs include Ableton Live, Cubase, FL Studio, Reason, Bitwig, Reaper, Mixbus, Studio One, Cakewalk, Logic Pro, and GarageBand.", "Learn more", "https://vcvrack.com/Rack"}, | |||||
{"You can learn more about VCV Rack by browsing the official manual.", "VCV Rack manual", "https://vcvrack.com/manual/"}, | |||||
{"Follow VCV Rack on Twitter for new module announcements, development news, and featured artists/music.", "Twitter @vcvrack", "https://twitter.com/vcvrack"}, | |||||
{"Patch cables in Rack can carry up to 16 signals. You can use this ability to build polyphonic patches using modules having the “Polyphonic” tag. Cables carrying more than 1 signal appear thicker than normal cables. To try out polyphony, add the VCV MIDI-to-CV module to your patch, right-click its panel, and select your desired number of polyphonic channels.", "Learn more about polyphony in VCV Rack", "https://vcvrack.com/manual/Polyphony"}, | |||||
{"Know C++ programming and want to create your own modules for Rack? Developing Rack modules is a great way to learn digital signal processing and quickly test your ideas with an easy-to-learn platform.\n\nDownload the Rack SDK and follow the development tutorial to get started.", "Plugin Development Tutorial", "https://vcvrack.com/manual/PluginDevelopmentTutorial"}, | |||||
{"Wondering how to use a particular module? Right-click its panel and choose “Info > User manual”.\n\nYou can also open the module's Info menu to view the module's tags, website, VCV Library page, and changelog, if available.", "", ""}, | |||||
{"Did you know that the VCV Library is integrated with ModularGrid? If a module is available as a hardware Eurorack module, right-click its panel and choose “Info > ModularGrid”, or click the “ModularGrid” link on its VCV Library page.\n\nOn ModularGrid.net, search for the VCV logo on certain module's entry pages.", "Example: Grayscale Permutation on ModularGrid", "https://www.modulargrid.net/e/grayscale-permutation-18hp"}, | |||||
{"When any context menu is open, you can " RACK_MOD_CTRL_NAME "+click a menu item to keep the menu open. This can be useful when browsing module presets or settings.", "", ""}, | |||||
// {"", "", ""}, | |||||
}; | |||||
static std::vector<TipInfo> getTipInfos() { | |||||
// Remember to use “smart quotes.” | |||||
return { | |||||
{string::translate("TipWindow.addModule"), "", ""}, | |||||
{string::f(string::translate("TipWindow.moveRack"), | |||||
widget::getKeyCommandName(0, RACK_MOD_ALT) + string::translate("key.click"), | |||||
string::translate("key.ctrl"), | |||||
string::translate("key.shift"), | |||||
string::translate("key.ctrl"), | |||||
string::translate("key.shift"), | |||||
string::translate("key.ctrl"), | |||||
widget::getKeyCommandName(GLFW_KEY_EQUAL, RACK_MOD_CTRL), | |||||
widget::getKeyCommandName(GLFW_KEY_MINUS, RACK_MOD_CTRL)), | |||||
"", ""}, | |||||
{string::translate("TipWindow.fullscreen"), string::translate("TipWindow.fullscreenButton"), "https://vcvrack.com/Recorder"}, | |||||
{string::translate("TipWindow.library"), string::translate("TipWindow.libraryButton"), "https://library.vcvrack.com/"}, | |||||
{string::translate("TipWindow.donation"), string::translate("TipWindow.libraryButton"), "https://library.vcvrack.com/"}, | |||||
{string::translate("TipWindow.daw"), string::translate("TipWindow.learnMore"), "https://vcvrack.com/Rack"}, | |||||
{string::translate("TipWindow.manual"), string::translate("TipWindow.manualButton"), "https://vcvrack.com/manual/"}, | |||||
{string::translate("TipWindow.twitter"), string::translate("TipWindow.twitterHandle"), "https://twitter.com/vcvrack"}, | |||||
{string::translate("TipWindow.polyphony"), string::translate("TipWindow.polyphonyButton"), "https://vcvrack.com/manual/Polyphony"}, | |||||
{string::translate("TipWindow.develop"), string::translate("TipWindow.developButton"), "https://vcvrack.com/manual/PluginDevelopmentTutorial"}, | |||||
{string::translate("TipWindow.moduleManual"), "", ""}, | |||||
{string::translate("TipWindow.modularGrid"), string::translate("TipWindow.modularGridButton"), "https://www.modulargrid.net/e/grayscale-permutation-18hp"}, | |||||
{string::f(string::translate("TipWindow.menuKeepOpen"), | |||||
widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("key.click")), | |||||
"", ""}, | |||||
// {"", "", ""}, | |||||
}; | |||||
} | |||||
struct TipWindow : widget::OpaqueWidget { | struct TipWindow : widget::OpaqueWidget { | ||||
@@ -74,7 +87,7 @@ struct TipWindow : widget::OpaqueWidget { | |||||
// header->box.size.x = box.size.x - 2*margin; | // header->box.size.x = box.size.x - 2*margin; | ||||
header->box.size.y = 20; | header->box.size.y = 20; | ||||
header->fontSize = 20; | header->fontSize = 20; | ||||
header->text = "Welcome to " + APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION; | |||||
header->text = string::f(string::translate("TipWindow.welcome"), APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION); | |||||
layout->addChild(header); | layout->addChild(header); | ||||
label = new ui::Label; | label = new ui::Label; | ||||
@@ -108,7 +121,7 @@ struct TipWindow : widget::OpaqueWidget { | |||||
ui::OptionButton* showButton = new ui::OptionButton; | ui::OptionButton* showButton = new ui::OptionButton; | ||||
showButton->box.size.x = 200; | showButton->box.size.x = 200; | ||||
showButton->text = "Show tips at startup"; | |||||
showButton->text = string::translate("TipWindow.startup"); | |||||
showButton->quantity = &showQuantity; | showButton->quantity = &showQuantity; | ||||
buttonLayout->addChild(showButton); | buttonLayout->addChild(showButton); | ||||
@@ -120,7 +133,7 @@ struct TipWindow : widget::OpaqueWidget { | |||||
}; | }; | ||||
PreviousButton* prevButton = new PreviousButton; | PreviousButton* prevButton = new PreviousButton; | ||||
prevButton->box.size.x = buttonWidth; | prevButton->box.size.x = buttonWidth; | ||||
prevButton->text = "â—€ Previous"; | |||||
prevButton->text = "â—€ " + string::translate("TipWindow.previous"); | |||||
prevButton->tipWindow = this; | prevButton->tipWindow = this; | ||||
buttonLayout->addChild(prevButton); | buttonLayout->addChild(prevButton); | ||||
@@ -132,7 +145,7 @@ struct TipWindow : widget::OpaqueWidget { | |||||
}; | }; | ||||
NextButton* nextButton = new NextButton; | NextButton* nextButton = new NextButton; | ||||
nextButton->box.size.x = buttonWidth; | nextButton->box.size.x = buttonWidth; | ||||
nextButton->text = "â–¶ Next"; | |||||
nextButton->text = "â–¶ " + string::translate("TipWindow.next"); | |||||
nextButton->tipWindow = this; | nextButton->tipWindow = this; | ||||
buttonLayout->addChild(nextButton); | buttonLayout->addChild(nextButton); | ||||
@@ -144,7 +157,7 @@ struct TipWindow : widget::OpaqueWidget { | |||||
}; | }; | ||||
CloseButton* closeButton = new CloseButton; | CloseButton* closeButton = new CloseButton; | ||||
closeButton->box.size.x = buttonWidth; | closeButton->box.size.x = buttonWidth; | ||||
closeButton->text = "âś– Close"; | |||||
closeButton->text = "âś– " + string::translate("TipWindow.close"); | |||||
closeButton->tipWindow = this; | closeButton->tipWindow = this; | ||||
buttonLayout->addChild(closeButton); | buttonLayout->addChild(closeButton); | ||||
@@ -155,6 +168,8 @@ struct TipWindow : widget::OpaqueWidget { | |||||
} | } | ||||
void advanceTip(int delta = 1) { | void advanceTip(int delta = 1) { | ||||
std::vector<TipInfo> tipInfos = getTipInfos(); | |||||
// Increment tip index | // Increment tip index | ||||
settings::tipIndex = math::eucMod(settings::tipIndex + delta, (int) tipInfos.size()); | settings::tipIndex = math::eucMod(settings::tipIndex + delta, (int) tipInfos.size()); | ||||
@@ -15,9 +15,7 @@ std::string PortInfo::getName() { | |||||
std::string PortInfo::getFullName() { | std::string PortInfo::getFullName() { | ||||
std::string name = getName(); | std::string name = getName(); | ||||
name += " "; | |||||
name += (type == Port::INPUT) ? "input" : "output"; | |||||
return name; | |||||
return string::f((type == Port::INPUT) ? string::translate("PortInfo.input") : string::translate("PortInfo.output"), name); | |||||
} | } | ||||
@@ -114,7 +114,7 @@ void logIn(std::string email, std::string password) { | |||||
return; | return; | ||||
DEFER({updateMutex.unlock();}); | DEFER({updateMutex.unlock();}); | ||||
loginStatus = "Logging in..."; | |||||
loginStatus = string::translate("library.loggingIn"); | |||||
json_t* reqJ = json_object(); | json_t* reqJ = json_object(); | ||||
json_object_set_new(reqJ, "email", json_string(email.c_str())); | json_object_set_new(reqJ, "email", json_string(email.c_str())); | ||||
json_object_set_new(reqJ, "password", json_string(password.c_str())); | json_object_set_new(reqJ, "password", json_string(password.c_str())); | ||||
@@ -123,7 +123,7 @@ void logIn(std::string email, std::string password) { | |||||
json_decref(reqJ); | json_decref(reqJ); | ||||
if (!resJ) { | if (!resJ) { | ||||
loginStatus = "No response from server"; | |||||
loginStatus = string::translate("library.noResponse"); | |||||
return; | return; | ||||
} | } | ||||
DEFER({json_decref(resJ);}); | DEFER({json_decref(resJ);}); | ||||
@@ -137,7 +137,7 @@ void logIn(std::string email, std::string password) { | |||||
json_t* tokenJ = json_object_get(resJ, "token"); | json_t* tokenJ = json_object_get(resJ, "token"); | ||||
if (!tokenJ) { | if (!tokenJ) { | ||||
loginStatus = "No token in response"; | |||||
loginStatus = string::translate("library.noToken"); | |||||
return; | return; | ||||
} | } | ||||
@@ -172,14 +172,14 @@ void checkUpdates() { | |||||
if (isSyncing) | if (isSyncing) | ||||
return; | return; | ||||
updateStatus = "Querying for updates..."; | |||||
updateStatus = string::translate("library.queryingUpdates"); | |||||
// Check user token | // Check user token | ||||
std::string userUrl = API_URL + "/user"; | std::string userUrl = API_URL + "/user"; | ||||
json_t* userResJ = network::requestJson(network::METHOD_GET, userUrl, NULL, getTokenCookies()); | json_t* userResJ = network::requestJson(network::METHOD_GET, userUrl, NULL, getTokenCookies()); | ||||
if (!userResJ) { | if (!userResJ) { | ||||
WARN("Request for user account failed"); | WARN("Request for user account failed"); | ||||
updateStatus = "Could not query user account"; | |||||
updateStatus = string::translate("library.queryAccountFailed"); | |||||
return; | return; | ||||
} | } | ||||
DEFER({json_decref(userResJ);}); | DEFER({json_decref(userResJ);}); | ||||
@@ -202,7 +202,7 @@ void checkUpdates() { | |||||
json_decref(manifestsReq); | json_decref(manifestsReq); | ||||
if (!manifestsResJ) { | if (!manifestsResJ) { | ||||
WARN("Request for library manifests failed"); | WARN("Request for library manifests failed"); | ||||
updateStatus = "Could not query plugin manifests"; | |||||
updateStatus = string::translate("library.queryManifestsFailed"); | |||||
return; | return; | ||||
} | } | ||||
DEFER({json_decref(manifestsResJ);}); | DEFER({json_decref(manifestsResJ);}); | ||||
@@ -212,7 +212,7 @@ void checkUpdates() { | |||||
json_t* modulesResJ = network::requestJson(network::METHOD_GET, modulesUrl, NULL, getTokenCookies()); | json_t* modulesResJ = network::requestJson(network::METHOD_GET, modulesUrl, NULL, getTokenCookies()); | ||||
if (!modulesResJ) { | if (!modulesResJ) { | ||||
WARN("Request for user's modules failed"); | WARN("Request for user's modules failed"); | ||||
updateStatus = "Could not query user's modules"; | |||||
updateStatus = string::translate("library.queryModulesFailed"); | |||||
return; | return; | ||||
} | } | ||||
DEFER({json_decref(modulesResJ);}); | DEFER({json_decref(modulesResJ);}); | ||||
@@ -150,9 +150,9 @@ void Port::setChannel(int channel) { | |||||
std::string Port::getChannelName(int channel) { | std::string Port::getChannelName(int channel) { | ||||
if (channel < 0) | if (channel < 0) | ||||
return "All channels"; | |||||
return string::translate("midi.allChannels"); | |||||
else | else | ||||
return string::f("Channel %d", channel + 1); | |||||
return string::f(string::translate("midi.channelNum"), channel + 1); | |||||
} | } | ||||
json_t* Port::toJson() { | json_t* Port::toJson() { | ||||
@@ -145,7 +145,7 @@ void Manager::saveDialog() { | |||||
save(path); | save(path); | ||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
std::string message = string::f("Could not save patch: %s", e.what()); | |||||
std::string message = string::f(string::translate("patch.saveFailed"), e.what()); | |||||
osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str()); | ||||
return; | return; | ||||
} | } | ||||
@@ -195,7 +195,7 @@ void Manager::saveAsDialog(bool setPath) { | |||||
save(path); | save(path); | ||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
std::string message = string::f("Could not save patch: %s", e.what()); | |||||
std::string message = string::f(string::translate("patch.saveFailed"), e.what()); | |||||
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | ||||
return; | return; | ||||
} | } | ||||
@@ -206,14 +206,14 @@ void Manager::saveAsDialog(bool setPath) { | |||||
void Manager::saveTemplateDialog() { | void Manager::saveTemplateDialog() { | ||||
// Even if <user>/template.vcv doesn't exist, this message is still valid because it overrides the <system>/template.vcv patch. | // Even if <user>/template.vcv doesn't exist, this message is still valid because it overrides the <system>/template.vcv patch. | ||||
if (!osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Overwrite template patch?")) | |||||
if (!osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, string::translate("patch.overwriteTemplate").c_str())) | |||||
return; | return; | ||||
try { | try { | ||||
save(templatePath); | save(templatePath); | ||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
std::string message = string::f("Could not save template patch: %s", e.what()); | |||||
std::string message = string::f(string::translate("patch.saveTemplateFailed"), e.what()); | |||||
osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str()); | ||||
return; | return; | ||||
} | } | ||||
@@ -316,7 +316,7 @@ void Manager::loadTemplate() { | |||||
load(factoryTemplatePath); | load(factoryTemplatePath); | ||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
std::string message = string::f("Could not load system template patch, clearing rack: %s", e.what()); | |||||
std::string message = string::f(string::translate("patch.loadTemplateFailed"), e.what()); | |||||
osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str()); | ||||
clear(); | clear(); | ||||
@@ -331,7 +331,7 @@ void Manager::loadTemplate() { | |||||
void Manager::loadTemplateDialog() { | void Manager::loadTemplateDialog() { | ||||
if (!promptClear("The current patch is unsaved. Clear it and start a new patch?")) { | |||||
if (!promptClear(string::translate("patch.loadTemplateConfirm"))) { | |||||
return; | return; | ||||
} | } | ||||
loadTemplate(); | loadTemplate(); | ||||
@@ -373,7 +373,7 @@ void Manager::loadAction(std::string path) { | |||||
load(path); | load(path); | ||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
std::string message = string::f("Could not load patch: %s", e.what()); | |||||
std::string message = string::f(string::translate("patch.loadFailed"), e.what()); | |||||
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | ||||
return; | return; | ||||
} | } | ||||
@@ -385,7 +385,7 @@ void Manager::loadAction(std::string path) { | |||||
void Manager::loadDialog() { | void Manager::loadDialog() { | ||||
if (!promptClear("The current patch is unsaved. Clear it and open a new patch?")) | |||||
if (!promptClear(string::translate("patch.loadConfirm"))) | |||||
return; | return; | ||||
std::string dir; | std::string dir; | ||||
@@ -415,7 +415,7 @@ void Manager::loadDialog() { | |||||
void Manager::loadPathDialog(std::string path) { | void Manager::loadPathDialog(std::string path) { | ||||
if (!promptClear("The current patch is unsaved. Clear it and open the new patch?")) | |||||
if (!promptClear(string::translate("patch.loadConfirm"))) | |||||
return; | return; | ||||
loadAction(path); | loadAction(path); | ||||
@@ -425,7 +425,7 @@ void Manager::loadPathDialog(std::string path) { | |||||
void Manager::revertDialog() { | void Manager::revertDialog() { | ||||
if (path == "") | if (path == "") | ||||
return; | return; | ||||
if (!promptClear("Revert patch to the last saved state?")) | |||||
if (!promptClear(string::translate("patch.revertConfirm"))) | |||||
return; | return; | ||||
loadAction(path); | loadAction(path); | ||||
@@ -577,11 +577,7 @@ bool Manager::checkUnavailableModulesJson(json_t* rootJ) { | |||||
if (!pluginModuleSlugs.empty()) { | if (!pluginModuleSlugs.empty()) { | ||||
// Ask user to open browser | // Ask user to open browser | ||||
std::string msg = "This patch includes modules that are not installed:"; | |||||
msg += "\n\n"; | |||||
msg += string::join(pluginModuleSlugs, "\n"); | |||||
msg += "\n\n"; | |||||
msg += "Show missing modules on the VCV Library?"; | |||||
std::string msg = string::f(string::translate("patch.unavailableModules"), string::join(pluginModuleSlugs, "\n")); | |||||
if (osdialog_message(OSDIALOG_WARNING, OSDIALOG_YES_NO, msg.c_str())) { | if (osdialog_message(OSDIALOG_WARNING, OSDIALOG_YES_NO, msg.c_str())) { | ||||
std::string url = "https://library.vcvrack.com/?modules="; | std::string url = "https://library.vcvrack.com/?modules="; | ||||
url += string::join(pluginModuleSlugs, ","); | url += string::join(pluginModuleSlugs, ","); | ||||
@@ -198,7 +198,9 @@ void Model::appendContextMenu(ui::Menu* menu, bool inBrowser) { | |||||
} | } | ||||
// Favorite | // Favorite | ||||
std::string favoriteRightText = inBrowser ? (RACK_MOD_CTRL_NAME "+click") : ""; | |||||
std::string favoriteRightText; | |||||
if (inBrowser) | |||||
favoriteRightText = widget::getKeyCommandName(0, RACK_MOD_CTRL) + string::translate("key.click"); | |||||
if (isFavorite()) | if (isFavorite()) | ||||
favoriteRightText += " " CHECKMARK_STRING; | favoriteRightText += " " CHECKMARK_STRING; | ||||
menu->addChild(createMenuItem(string::translate("Model.favorite"), favoriteRightText, | menu->addChild(createMenuItem(string::translate("Model.favorite"), favoriteRightText, | ||||
@@ -360,25 +360,25 @@ void TextField::createContextMenu() { | |||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
TextFieldCutItem* cutItem = new TextFieldCutItem; | TextFieldCutItem* cutItem = new TextFieldCutItem; | ||||
cutItem->text = "Cut"; | |||||
cutItem->text = string::translate("TextField.cut"); | |||||
cutItem->rightText = widget::getKeyCommandName(GLFW_KEY_X, RACK_MOD_CTRL); | cutItem->rightText = widget::getKeyCommandName(GLFW_KEY_X, RACK_MOD_CTRL); | ||||
cutItem->textField = this; | cutItem->textField = this; | ||||
menu->addChild(cutItem); | menu->addChild(cutItem); | ||||
TextFieldCopyItem* copyItem = new TextFieldCopyItem; | TextFieldCopyItem* copyItem = new TextFieldCopyItem; | ||||
copyItem->text = "Copy"; | |||||
copyItem->text = string::translate("TextField.copy"); | |||||
copyItem->rightText = widget::getKeyCommandName(GLFW_KEY_C, RACK_MOD_CTRL); | copyItem->rightText = widget::getKeyCommandName(GLFW_KEY_C, RACK_MOD_CTRL); | ||||
copyItem->textField = this; | copyItem->textField = this; | ||||
menu->addChild(copyItem); | menu->addChild(copyItem); | ||||
TextFieldPasteItem* pasteItem = new TextFieldPasteItem; | TextFieldPasteItem* pasteItem = new TextFieldPasteItem; | ||||
pasteItem->text = "Paste"; | |||||
pasteItem->text = string::translate("TextField.paste"); | |||||
pasteItem->rightText = widget::getKeyCommandName(GLFW_KEY_V, RACK_MOD_CTRL); | pasteItem->rightText = widget::getKeyCommandName(GLFW_KEY_V, RACK_MOD_CTRL); | ||||
pasteItem->textField = this; | pasteItem->textField = this; | ||||
menu->addChild(pasteItem); | menu->addChild(pasteItem); | ||||
TextFieldSelectAllItem* selectAllItem = new TextFieldSelectAllItem; | TextFieldSelectAllItem* selectAllItem = new TextFieldSelectAllItem; | ||||
selectAllItem->text = "Select all"; | |||||
selectAllItem->text = string::translate("TextField.selectAll"); | |||||
selectAllItem->rightText = widget::getKeyCommandName(GLFW_KEY_A, RACK_MOD_CTRL); | selectAllItem->rightText = widget::getKeyCommandName(GLFW_KEY_A, RACK_MOD_CTRL); | ||||
selectAllItem->textField = this; | selectAllItem->textField = this; | ||||
menu->addChild(selectAllItem); | menu->addChild(selectAllItem); | ||||
@@ -17,6 +17,7 @@ std::string getKeyName(int key) { | |||||
// glfwGetKeyName overrides | // glfwGetKeyName overrides | ||||
switch (key) { | switch (key) { | ||||
case GLFW_KEY_SPACE: return string::translate("key.space"); | case GLFW_KEY_SPACE: return string::translate("key.space"); | ||||
case GLFW_KEY_MINUS: return string::translate("key.minus"); | |||||
} | } | ||||
// Printable characters | // Printable characters | ||||
@@ -2,6 +2,7 @@ | |||||
"language": "English", | "language": "English", | ||||
"translators": "", | "translators": "", | ||||
"key.space": "Space", | "key.space": "Space", | ||||
"key.minus": "Minus", | |||||
"key.escape": "Escape", | "key.escape": "Escape", | ||||
"key.enter": "Enter", | "key.enter": "Enter", | ||||
"key.tab": "Tab", | "key.tab": "Tab", | ||||
@@ -19,6 +20,12 @@ | |||||
"key.ctrl": "Ctrl", | "key.ctrl": "Ctrl", | ||||
"key.shift": "Shift", | "key.shift": "Shift", | ||||
"key.alt": "Alt", | "key.alt": "Alt", | ||||
"key.click": "Click", | |||||
"key.rightClick": "Right-click", | |||||
"key.middleClick": "Middle-click", | |||||
"key.doubleClick": "Double-click", | |||||
"key.drag": "Drag", | |||||
"key.clickDrag": "Click+Drag", | |||||
"tag.Arpeggiator": "Arpeggiator", | "tag.Arpeggiator": "Arpeggiator", | ||||
"tag.Attenuator": "Attenuator", | "tag.Attenuator": "Attenuator", | ||||
"tag.Blank": "Blank", | "tag.Blank": "Blank", | ||||
@@ -259,13 +266,11 @@ | |||||
"Browser.allBrands": "All brands", | "Browser.allBrands": "All brands", | ||||
"Browser.brand": "Brand", | "Browser.brand": "Brand", | ||||
"Browser.allTags": "All tags", | "Browser.allTags": "All tags", | ||||
"Browser.tagsSelectMultiple": "click to select multiple", | |||||
"Browser.tagsSelectMultiple": " to select multiple", | |||||
"Browser.tags": "Tags", | "Browser.tags": "Tags", | ||||
"ParamWidget.history.setParam": "set parameter", | "ParamWidget.history.setParam": "set parameter", | ||||
"ParamWidget.initialize": "Initialize", | "ParamWidget.initialize": "Initialize", | ||||
"ParamWidget.doubleClick": "Double-click", | |||||
"ParamWidget.fine": "Fine adjust", | "ParamWidget.fine": "Fine adjust", | ||||
"ParamWidget.fineDrag": "drag", | |||||
"ParamWidget.unmap": "Unmap", | "ParamWidget.unmap": "Unmap", | ||||
"ParamWidget.history.reset": "reset parameter", | "ParamWidget.history.reset": "reset parameter", | ||||
"PortWidget.from": "From ", | "PortWidget.from": "From ", | ||||
@@ -275,13 +280,85 @@ | |||||
"PortWidget.cableId": "ID: %ld", | "PortWidget.cableId": "ID: %ld", | ||||
"PortWidget.setColor": "Set color", | "PortWidget.setColor": "Set color", | ||||
"PortWidget.deleteTopCable": "Delete top cable", | "PortWidget.deleteTopCable": "Delete top cable", | ||||
"PortWidget.click": "click", | |||||
"PortWidget.cloneTopCable": "Duplicate top cable", | "PortWidget.cloneTopCable": "Duplicate top cable", | ||||
"PortWidget.createCableTop": "Create cable on top", | "PortWidget.createCableTop": "Create cable on top", | ||||
"PortWidget.drag": "drag", | |||||
"PortWidget.createCable": "Create cable: ", | "PortWidget.createCable": "Create cable: ", | ||||
"PortWidget.clickDrag": "Click+drag", | |||||
"PortWidget.grabCable": " to grab cable", | "PortWidget.grabCable": " to grab cable", | ||||
"PortWidget.allCables": "All cables", | "PortWidget.allCables": "All cables", | ||||
"PortWidget.history.moveCable": "move cable" | |||||
"PortWidget.history.moveCable": "move cable", | |||||
"library.loggingIn": "Logging in...", | |||||
"library.noResponse": "No response from server", | |||||
"library.noToken": "No token in response", | |||||
"library.queryingUpdates": "Querying for updates...", | |||||
"library.queryAccountFailed": "Could not query user account", | |||||
"library.queryManifestsFailed": "Could not query plugin manifests", | |||||
"library.queryModulesFailed": "Could not query user's modules", | |||||
"patch.saveFailed": "Could not save patch: %s", | |||||
"patch.overwriteTemplate": "Overwrite template patch?", | |||||
"patch.saveTemplateFailed": "Could not save template patch: %s", | |||||
"patch.loadTemplateFailed": "Could not load system template patch, clearing rack: %s", | |||||
"patch.loadTemplateConfirm": "The current patch is unsaved. Clear it and start a new patch?", | |||||
"patch.loadFailed": "Could not load patch: %s", | |||||
"patch.loadConfirm": "The current patch is unsaved. Clear it and open a new patch?", | |||||
"patch.revertConfirm": "Revert patch to the last saved state?", | |||||
"patch.unavailableModules": "This patch includes modules that are not installed:\n\n%s\n\nShow missing modules on the VCV Library?", | |||||
"Switch.history.move": "move switch", | |||||
"Knob.history.move": "move knob", | |||||
"TextField.cut": "Cut", | |||||
"TextField.copy": "Copy", | |||||
"TextField.paste": "Paste", | |||||
"TextField.selectAll": "Select all", | |||||
"standalone.multipleInstances": "VCV Rack is already running. Multiple Rack instances are not supported.", | |||||
"standalone.resetSettings": "Reset settings to default?", | |||||
"standalone.resDir": "VCV Rack's resource directory \"%s\" does not exist. Make sure Rack is correctly installed and launched.", | |||||
"standalone.micPermission": "VCV Rack cannot access audio input because Microphone permission is blocked.\n\nGive permission to Rack by opening Apple's System Settings and enabling Privacy & Security > Microphone > %s.", | |||||
"standalone.crashed": "VCV Rack crashed during the last session, possibly due to a buggy module in your patch. Clear your patch and start over?", | |||||
"AudioDisplay.audioDriver": "Audio driver", | |||||
"AudioDisplay.driver": "Driver: ", | |||||
"AudioDisplay.noDriver": "No driver", | |||||
"AudioDisplay.noDevice": "No device", | |||||
"AudioDisplay.audioDevice": "Audio device", | |||||
"AudioDisplay.device": "Device: ", | |||||
"AudioDisplay.lockedByDevice": "Locked by device", | |||||
"AudioDisplay.sampleRate": "Sample rate", | |||||
"AudioDisplay.sampleRateColon": "Rate: ", | |||||
"AudioDisplay.blockSize": "Block size", | |||||
"AudioDisplay.blockSizeColon": "Block size: ", | |||||
"MidiDisplay.driver": "MIDI driver", | |||||
"MidiDisplay.noDriver": "No driver", | |||||
"MidiDisplay.device": "MIDI device", | |||||
"MidiDisplay.noDevice": "No device", | |||||
"MidiDisplay.channel": "MIDI channel", | |||||
"MidiDisplay.channel1": "Channel 1", | |||||
"midi.allChannels": "All channels", | |||||
"midi.channelNum": "Channel %d", | |||||
"TipWindow.addModule": "To add a module to your patch, right-click an empty rack space or press Enter. Then click and drag a module from the Module Browser into the desired rack space.\\n\\nTo select multiple modules, click and drag on empty rack space.", | |||||
"TipWindow.moveRack": "To move around your patch, use the scroll bars, drag while holding the middle mouse button, %s and drag, or hold the arrow keys. Arrow key movement speed can be adjusted by holding %s, %s, or %s+%s.\\n\\nTo zoom in and out, drag the Zoom slider in the View menu, hold %s and scroll, or press %s and %s.", | |||||
"TipWindow.fullscreen": "You can use Rack in fullscreen mode by selecting \u201cView > Fullscreen\u201c or pressing F11.\\n\\nIn fullscreen mode, the menu bar and scroll bars are hidden. This is ideal for screen recording with VCV Recorder.", | |||||
"TipWindow.fullscreenButton": "Get VCV Recorder", | |||||
"TipWindow.library": "You can browse thousands of modules on the VCV Library website.\\n\\nRegister for a VCV account, log into Rack using the Library menu, and browse the VCV Library to add or purchase modules. Keep all plugins up to date by clicking \u201cLibrary > Update all\u201d.", | |||||
"TipWindow.libraryButton": "VCV Library", | |||||
"TipWindow.donation": "Some developers of free plugins accept donations. Right-click your favorite module's panel and select \u201cInfo > Donate\u201d.\\n\\nYou can also donate via the module's VCV Library page.", | |||||
"TipWindow.daw": "Want to use VCV Rack in your DAW? VCV Rack Pro is available for VST2, VST3, Audio Unit, and CLAP hosts.\\n\\nSupported DAWs include Ableton Live, Cubase, FL Studio, Reason, Bitwig, Reaper, Mixbus, Studio One, Cakewalk, Logic Pro, and GarageBand.", | |||||
"TipWindow.learnMore": "Learn more", | |||||
"TipWindow.manual": "You can learn more about VCV Rack by browsing the official manual.", | |||||
"TipWindow.manualButton": "VCV Rack manual", | |||||
"TipWindow.twitter": "Follow VCV Rack on Twitter for new module announcements, development news, and featured artists/music.", | |||||
"TipWindow.twitterHandle": "Twitter @vcvrack", | |||||
"TipWindow.polyphony": "Patch cables in Rack can carry up to 16 signals. You can use this ability to build polyphonic patches using modules having the \u201cPolyphonic\u201d tag. Cables carrying more than 1 signal appear thicker than normal cables. To try out polyphony, add the VCV MIDI-to-CV module to your patch, right-click its panel, and select your desired number of polyphonic channels.", | |||||
"TipWindow.polyphonyButton": "Learn more about polyphony in VCV Rack", | |||||
"TipWindow.develop": "Know C++ programming and want to create your own modules for Rack? Developing Rack modules is a great way to learn digital signal processing and quickly test your ideas with an easy-to-learn platform.\\n\\nDownload the Rack SDK and follow the development tutorial to get started.", | |||||
"TipWindow.developButton": "Plugin Development Tutorial", | |||||
"TipWindow.moduleManual": "Wondering how to use a particular module? Right-click its panel and choose \u201cInfo > User manual\u201d.\\n\\nYou can also open the module's Info menu to view the module's tags, website, VCV Library page, and changelog, if available.", | |||||
"TipWindow.modularGrid": "Did you know that the VCV Library is integrated with ModularGrid? If a module is available as a hardware Eurorack module, right-click its panel and choose \u201cInfo > ModularGrid\u201d, or click the \u201cModularGrid\u201d link on its VCV Library page.\\n\\nOn ModularGrid.net, search for the VCV logo on certain module's entry pages.", | |||||
"TipWindow.modularGridButton": "Example: Grayscale Permutation on ModularGrid", | |||||
"TipWindow.menuKeepOpen": "When any context menu is open, you can %s a menu item to keep the menu open. This can be useful when browsing module presets or settings.", | |||||
"TipWindow.welcome": "Welcome to %s", | |||||
"TipWindow.startup": "Show tips at startup", | |||||
"TipWindow.previous": "Previous", | |||||
"TipWindow.next": "Next", | |||||
"TipWindow.close": "Close", | |||||
"PortInfo.input": "%s input", | |||||
"PortInfo.output": "%s output", | |||||
"ModuleLightWidget.light": "%s light" | |||||
} | } |