Complete language support to all GUI tabs#873
Conversation
Wraps all user-facing string literals in the three Schedules sub-tabs (Schedule Sets, Schedules, Other Schedules) with Qt tr() calls to enable multi-language support, addressing issue openstudiocoalition#680. Files modified: - ScheduleSetInspectorView: 12 section/field labels - SchedulesView: 23 strings across ScheduleTabContent, ScheduleTabDefault, NewProfileView, DefaultScheduleDayView, SpecialScheduleDayView, ScheduleRuleView, ScheduleRulesetNameWidget, MonthView - ScheduleFileInspectorView: 15 field labels and combo box items - ScheduleDialog: 10 strings including runtime-appended values - ScheduleDayView + .hpp: 6 button/label strings; adds Q_DECLARE_TR_FUNCTIONS to 3 QGraphicsItem subclasses for tooltip tr() - ScheduleCompactInspectorView: 2 labels - ScheduleConstantInspectorView: 2 labels - ScheduleOthersController: 1 error message - ScheduleOthersView: 3 sidebar type names via QCoreApplication::translate() - MainRightColumnController: 9 sidebar nav strings via tr().toStdString() Adds Spanish translations for all new strings to OpenStudioApp_es.ts. Day-of-week single-letter buttons S/T marked unfinished pending source-level disambiguation (tr("S", "Sunday") etc.). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…penstudiocoalition#680) Wraps user-facing string literals with tr() across all major tabs and adds a complete Spanish (es) translation file as proof of concept for multi-language support, addressing issue openstudiocoalition#680. Tabs covered: Site/Weather Data, Construction Sets, Materials, Schedules, Thermal Zones, Space Types, Loads, Facility, HVAC Systems, Inspector panel IDD fields, Output Variables (1051 names), Simulation Settings, Measures, Run Simulation, and Results Summary. Key patterns established: - tr() for compile-time strings in Q_OBJECT classes - QCoreApplication::translate(IDD/OutputVariables/TaxonomyCategories) for runtime strings from the OpenStudio SDK and taxonomy.xml - addItem(tr(Display), EnglishData) + currentData() for model-bound combo boxes where SDK values must stay in English - Bilingual display format for IDD fields and output variable names so engineers can cross-reference EnergyPlus documentation without switching the application language translations/OpenStudioApp_es.ts grows from ~200 to 3017 entries. New languages only require a new .ts file with no further C++ changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…#680) Adds Translation_GTest.cpp to the OpenStudioApp test target, covering: Translation_ts suite (no build-path dependency, uses .ts source file): - ValidXml: verifies OpenStudioApp_es.ts parses as well-formed XML - HasExpectedContexts: checks all new translation contexts are present (IDD, OutputVariables, TaxonomyCategories, SimSettingsView, RunView, etc.) - TranslationCountIsSubstantial: guards against accidental file truncation - IddContextHasEntries: IDD context has >50 field-name translations - OutputVariablesContextHasEntries: OutputVariables context has >=1000 entries - TaxonomyCategoriesContextHasEntries: taxonomy categories are present Translation_qm suite (requires compiled .qm, skipped gracefully if absent): - QmFileLoads: QTranslator::load() succeeds for OpenStudioApp_es.qm - SpanishSimSettingsStringsTranslated: spot-checks Simulation Settings labels - SpanishRunViewStringsTranslated: spot-checks Run Simulation labels - TaxonomyCategoriesTranslated: spot-checks library sidebar category names - OutputVariablesSampleTranslated: spot-checks output variable name translations - EnglishStringsReturnedWithoutTranslator: verifies English fallback when no QTranslator is installed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
|
This is awesome @Ski90Moo! I will check this out! |
|
Thank you @macumber. It is rough, but a good start. I would like your feedback on the html issue. |
There was a problem hiding this comment.
Pull request overview
This PR expands internationalization across the OpenStudio Application UI by wrapping user-facing strings with Qt translation APIs and adding a significantly more complete Spanish translation as a proof of concept (Issue #680). It also introduces tests to validate the .ts file structure/content and (optionally) runtime .qm translation loading.
Changes:
- Wrapped many GUI string literals in
tr()/QCoreApplication::translate()across tabs, inspectors, and grid views. - Added bilingual display formatting for SDK-driven names (eg. Output Variables, IDD fields) to show
Translated (English)when non-English. - Added a new translation-focused GTest suite and wired it into the app test target.
Reviewed changes
Copilot reviewed 98 out of 100 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared_gui_components/WorkflowView.cpp | Translates “drop measure” placeholder text. |
| src/shared_gui_components/WorkflowController.cpp | Translates section headers for measure categories. |
| src/shared_gui_components/OSGridController.cpp | Translates “Custom” category label and apply-button text. |
| src/shared_gui_components/LocalLibraryView.cpp | Translates tooltips/button labels in the local library footer. |
| src/shared_gui_components/LocalLibraryController.cpp | Translates taxonomy category names at render time. |
| src/openstudio_lib/WindowMaterialSimpleGlazingSystemInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialShadeInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialScreenInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialGlazingRefractionExtinctionMethodInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialGlazingInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialGasMixtureInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialGasInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialDaylightRedirectionDeviceInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WindowMaterialBlindInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/WaterUseEquipmentInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/VRFGraphicsItems.cpp | Translates VRF drag/drop placeholder labels. |
| src/openstudio_lib/VariablesTabView.cpp | Adds translated+bilingual output variable display and localized frequency display with English data values. |
| src/openstudio_lib/ThermalZonesTabView.cpp | Translates main tab title. |
| src/openstudio_lib/ThermalZonesGridView.cpp | Translates grid headers/categories via QCoreApplication::translate. |
| src/openstudio_lib/SteamEquipmentInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/StandardsInformationMaterialWidget.cpp | Translates standards/measure-tags labels. |
| src/openstudio_lib/StandardsInformationConstructionWidget.cpp | Translates standards/measure-tags labels. |
| src/openstudio_lib/SpaceTypesTabView.cpp | Translates main tab title. |
| src/openstudio_lib/SpaceTypesGridView.cpp | Translates headers/categories/filter labels; some tooltips still hardcoded. |
| src/openstudio_lib/SpacesTabController.cpp | Translates spaces sub-tab titles. |
| src/openstudio_lib/SpacesSurfacesGridView.cpp | Translates grid headers/categories. |
| src/openstudio_lib/SpacesSubtabGridView.cpp | Translates filter labels; stores English keys in combo box data for SDK values. |
| src/openstudio_lib/SpacesSubsurfacesGridView.cpp | Translates grid headers/categories. |
| src/openstudio_lib/SpacesSpacesGridView.cpp | Translates grid headers/categories. |
| src/openstudio_lib/SpacesShadingGridView.cpp | Translates grid headers/categories. |
| src/openstudio_lib/SpacesLoadsGridView.cpp | Translates grid headers/categories. |
| src/openstudio_lib/SpacesInteriorPartitionsGridView.cpp | Translates grid headers/categories. |
| src/openstudio_lib/SimSettingsTabController.cpp | Translates “Simulation Settings” tab title. |
| src/openstudio_lib/ServiceWaterGridItems.cpp | Translates tooltips and drop-zone placeholder text. |
| src/openstudio_lib/ScriptsTabView.cpp | Translates “Measures” tab title and sync button strings. |
| src/openstudio_lib/SchedulesTabView.cpp | Translates schedules sub-tab title. |
| src/openstudio_lib/SchedulesTabController.cpp | Translates schedules sub-tab titles. |
| src/openstudio_lib/ScheduleSetInspectorView.cpp | Translates schedule set inspector section labels. |
| src/openstudio_lib/ScheduleOthersView.cpp | Translates “Schedule Others” type names returned to the list view. |
| src/openstudio_lib/ScheduleOthersController.cpp | Translates unsupported-action message text. |
| src/openstudio_lib/ScheduleFileInspectorView.cpp | Translates labels and attempts to translate separator choices (but bind() will overwrite). |
| src/openstudio_lib/ScheduleDialog.cpp | Translates dialog labels and “None/unitless” strings. |
| src/openstudio_lib/ScheduleDayView.hpp | Adds Q_DECLARE_TR_FUNCTIONS for QGraphicsItem subclasses to enable tr(). |
| src/openstudio_lib/ScheduleDayView.cpp | Translates schedule day UI strings/tooltips and keyboard prompt text. |
| src/openstudio_lib/ScheduleConstantInspectorView.cpp | Translates labels (one includes leading whitespace in the source string). |
| src/openstudio_lib/ScheduleCompactInspectorView.cpp | Translates labels. |
| src/openstudio_lib/RunTabView.cpp | Translates run tab title, button/checkbox labels, and status text. |
| src/openstudio_lib/ResultsTabView.cpp | Translates Results UI labels and some report names; “Custom Report N” uses concatenation. |
| src/openstudio_lib/ResultsTabController.cpp | Translates “Results Summary” tab title. |
| src/openstudio_lib/RefrigerationGraphicsItems.cpp | Translates refrigeration drop-zone placeholder text. |
| src/openstudio_lib/PeopleInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/OtherEquipmentInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/OSItemSelectorButtons.cpp | Translates toolbar button tooltips. |
| src/openstudio_lib/OSDropZone.hpp | Changes default text arg to empty so ctor can apply translated default. |
| src/openstudio_lib/OSDropZone.cpp | Applies translated default “Drag From Library” when text is empty. |
| src/openstudio_lib/OSDocument.cpp | Translates left-side main tab button labels. |
| src/openstudio_lib/MaterialsView.cpp | Translates materials category names returned to list view. |
| src/openstudio_lib/MaterialRoofVegetationInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/MaterialNoMassInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/MaterialInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/MaterialAirGapInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/LuminaireInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/LoopLibraryDialog.cpp | Translates HVAC library dialog titles/buttons/system names. |
| src/openstudio_lib/LoadsView.cpp | Translates loads category names returned to list view. |
| src/openstudio_lib/LightsInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/library/geometry_editor_start.html | Adds an in-HTML Spanish translation dictionary + DOM rewrite for headings/paragraphs. |
| src/openstudio_lib/InternalMassInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/HVACSystemsTabView.cpp | Translates main tab title. |
| src/openstudio_lib/HVACSystemsController.cpp | Translates system selector entries and HVAC type labels; translates some drop-zone text. |
| src/openstudio_lib/HotWaterEquipmentInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/GridItem.cpp | Translates drop-zone placeholder and “Supply/Demand Equipment” labels. |
| src/openstudio_lib/GeometryTabController.cpp | Translates geometry tab title and sub-tab titles. |
| src/openstudio_lib/GeometryPreviewView.cpp | Injects locale into embedded web content; translates some UI strings. |
| src/openstudio_lib/GeometryEditorView.cpp | Injects locale into embedded editor; translates most UI strings and some dialogs. |
| src/openstudio_lib/GasEquipmentInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/FacilityTabView.cpp | Translates facility tab title. |
| src/openstudio_lib/FacilityTabController.cpp | Translates facility sub-tab titles. |
| src/openstudio_lib/FacilityStoriesGridView.cpp | Translates grid headers/categories and filter label. |
| src/openstudio_lib/FacilityShadingGridView.cpp | Translates grid headers/categories and uses English keys in filter combo data. |
| src/openstudio_lib/FacilityExteriorEquipmentGridView.cpp | Translates grid headers/categories and view titles. |
| src/openstudio_lib/ElectricEquipmentInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/ConstructionsView.cpp | Translates constructions category names returned to list view. |
| src/openstudio_lib/ConstructionsTabController.cpp | Translates constructions sub-tab titles and main tab title. |
| src/openstudio_lib/ConstructionInternalSourceInspectorView.cpp | Translates inspector labels and drop-zone text (and file gained a BOM). |
| src/openstudio_lib/ConstructionInspectorView.cpp | Translates inspector labels and drop-zone text. |
| src/openstudio_lib/ConstructionFfactorGroundFloorInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/ConstructionCfactorUndergroundWallInspectorView.cpp | Translates inspector labels (and file gained a BOM). |
| src/openstudio_lib/ConstructionAirBoundaryInspectorView.cpp | Translates inspector labels and comments (and file gained a BOM). |
| src/openstudio_lib/BuildingInspectorView.cpp | Translates building inspector labels. |
| src/openstudio_app/test/Translation_GTest.cpp | Adds tests to validate .ts content and optionally .qm runtime translation. |
| src/openstudio_app/CMakeLists.txt | Adds Translation_GTest.cpp to the test sources. |
| src/model_editor/InspectorGadget.cpp | Adds bilingual IDD field display names with runtime translation lookup. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| m_combobox = new OSComboBox2(); | ||
| m_combobox->setFixedWidth(200); | ||
| connect(m_combobox, static_cast<void (OSComboBox2::*)(const QString&)>(&OSComboBox2::currentTextChanged), this, &VariableListItem::indexChanged); | ||
| populateFrequencyComboBox(m_combobox); | ||
| if (m_variable) { | ||
| // m_combobox->bind(*m_variable, "reportingFrequency"); | ||
| m_combobox->bind<std::string>( | ||
| *m_variable, static_cast<std::string (*)(const std::string&)>(&openstudio::toString), | ||
| std::bind(&model::OutputVariable::reportingFrequencyValues), std::bind(&model::OutputVariable::reportingFrequency, m_variable.get_ptr()), | ||
| std::bind(&model::OutputVariable::setReportingFrequency, m_variable.get_ptr(), std::placeholders::_1), | ||
| boost::optional<NoFailAction>(std::bind(&model::OutputVariable::resetReportingFrequency, m_variable.get_ptr())), | ||
| boost::optional<BasicQuery>(std::bind(&model::OutputVariable::isReportingFrequencyDefaulted, m_variable.get_ptr()))); | ||
| const int idx = m_combobox->findData(QString::fromStdString(m_variable->reportingFrequency())); | ||
| if (idx >= 0) { | ||
| m_combobox->setCurrentIndex(idx); | ||
| } |
There was a problem hiding this comment.
Fixed — moved connect() to after populateFrequencyComboBox() and after the initial index is set, so populating the list doesn't fire indexChanged() during construction.
| m_columnSeparator = new OSComboBox2(); | ||
| m_columnSeparator->addItem("Comma"); | ||
| m_columnSeparator->addItem("Tab"); | ||
| m_columnSeparator->addItem("Space"); | ||
| m_columnSeparator->addItem("Semicolon"); | ||
| m_columnSeparator->addItem(tr("Comma")); | ||
| m_columnSeparator->addItem(tr("Tab")); | ||
| m_columnSeparator->addItem(tr("Space")); | ||
| m_columnSeparator->addItem(tr("Semicolon")); | ||
| m_columnSeparator->setEnabled(true); | ||
| vLayout->addWidget(m_columnSeparator); |
There was a problem hiding this comment.
Fixed — removed the pre-bind addItem(tr(...)) calls. The bind() call supplies its own choices list and overwrites them anyway.
| checkbox->setToolTip(tr("Check to select all rows")); | ||
| connect(checkbox.data(), &OSSelectAllCheckBox::checkStateChanged, this, &SpaceTypesGridController::onSelectAllStateChanged); | ||
| connect(this, &SpaceTypesGridController::gridRowSelectionChanged, checkbox.data(), &OSSelectAllCheckBox::onGridRowSelectionChanged); | ||
| addSelectColumn(Heading(QString(SELECTED), false, false, checkbox), "Check to select this row", DataSource(allLoads, true)); |
There was a problem hiding this comment.
Done — all C++ hardcoded strings are now wrapped with tr():
SpaceTypesGridView.cpptooltip was alreadytr()-wrapped earlier in the PRGeometryEditorView.cpp"Debug" button label wrapped (commit d239658)GeometryPreviewView.cppGeometry Diagnostics checkbox tooltip wrapped (commit d239658)
The HTML geometry files (geometry_editor_start.html, geometry_preview.html) still contain untranslated JavaScript strings, but these require the QWebEnginePage::runJavaScript() injection approach — deferred to a follow-up PR by agreement with macumber.
| m_geometryDiagnosticsBox->setText(tr("Geometry Diagnostics")); | ||
| m_geometryDiagnosticsBox->setChecked(verboseOutput); | ||
| m_geometryDiagnosticsBox->setToolTip( | ||
| "Enables adjacency issues. Enables checks for Surface/Space Convexity, due to this the ThreeJS export is slightly slower"); |
| m_refreshBtn(new QPushButton(tr("Refresh"))), | ||
| m_previewBtn(new QPushButton(tr("Preview OSM"))), | ||
| m_mergeBtn(new QPushButton(tr("Merge with Current OSM"))), | ||
| m_debugBtn(new QPushButton("Debug")) { |
| if ((startingIndex == -1) || (endingIndex == -1) || (startingIndex >= endingIndex)) { | ||
| m_comboBox->addItem(QString("Custom Report ") + QString::number(num), fullPathString); | ||
| m_comboBox->addItem(tr("Custom Report ") + QString::number(num), fullPathString); | ||
| } else { |
There was a problem hiding this comment.
Fixed — changed to tr("Custom Report %1").arg(num).
| @@ -1,4 +1,4 @@ | |||
| /*********************************************************************************************************************** | |||
| /*********************************************************************************************************************** | |||
There was a problem hiding this comment.
Done — removed the UTF-8 BOM from 27 inspector view .cpp files (commit d239658). The BOM was introduced by the editor when files were modified during i18n work; it has no effect at runtime but is incorrect for UTF-8 source files.
| // Value | ||
|
|
||
| label = new QLabel(" Value: "); | ||
| label = new QLabel(tr(" Value: ")); | ||
| label->setObjectName("H2"); | ||
| mainGridLayout->addWidget(label, row++, 0); |
There was a problem hiding this comment.
Fixed — changed to tr("Value: ").
| @@ -1474,7 +1486,7 @@ void EditorWebView::mergeExport() { | |||
| QMessageBox::warning(this, "Merging Models", errorsAndWarnings); | |||
There was a problem hiding this comment.
Done — both instances of "Merging Models" wrapped with tr() in commit d74d92b.
| // | ||
| // To add or refine translations: add entries to the IDD context in | ||
| // OpenStudioApp_<lang>.ts and recompile with lrelease — no C++ changes needed. | ||
| static QString iddFieldDisplayName(const std::string& englishName) { |
There was a problem hiding this comment.
This is really cool, I just wonder how we will be able to maintain this. The Translation test could possibly loop over each IddObject and verify that there is an entry for each one?
Same concern for the OutputVariables
There was a problem hiding this comment.
That was one my concerns as well. I did a handful of them, but I could imagine the list of these objects would be enormous and difficult to maintain. It also makes the .ts file huge; no small task for the language contributors to review. I envision it would be more of a reactionary process whereby the contributors review the general GUI stuff, but we would get feedback as users stumble upon poorly translated objects.
The OutputVariables should be complete though. I will look into modifying the Translation test.
There was a problem hiding this comment.
Good call. The OutputVariable gap is fixed — the original entries only had Zone X prefixed names, but outputVariableNames() returns bare names (e.g. Lights Electricity Energy). Added 359 bare-name entries auto-derived from the Zone translations, plus specific Site/Surface/People/Daylighting entries.
For IDD fields: all fields now have translations across all 18 language .ts files, generated via Claude API batch translation. Two complementary tools maintain coverage:
add_idd_skeleton.py— developer maintenance tool. Run after an SDK update to add skeleton entries for any new IDD fields, then runtranslate_skeleton.pyto batch-translate them.Translation_ts.IddCoverageAllLanguagesGTest — usesIddFactory::instance()to enumerate every IddObject and its fields, then checks that each unique field name has a source entry in the IDD context of everyOpenStudioApp_*.tsfile. Fails with a per-language list of missing fields if any file is out of sync.
The test detects drift; the script fixes it. They run independently.
| <meta charset=utf-8> | ||
| <title>OpenStudio Geometry Editor</title> | ||
| <script> | ||
| var _translations = { |
There was a problem hiding this comment.
I'm not as thrilled about this, I would prefer something using the Qt facilities
There was a problem hiding this comment.
I will look into moving the translations out of the HTML into the .ts file.
There was a problem hiding this comment.
Reverted. We'll implement the proper Qt approach (injecting locale via QWebEnginePage::runJavaScript() and moving the strings into the .ts file) in a follow-up PR.
| @@ -1049,6 +1134,81 @@ Si le gustaría ver la AplicaciónOpenStudio traducido a algun otro lenguaje, le | |||
| <source>Online BCL</source> | |||
| <translation type="vanished">BCL en Linea</translation> | |||
There was a problem hiding this comment.
Could be a separate pull request but we probably need github actions to detect vanished or unfinished strings to maintain this
There was a problem hiding this comment.
Done — added .github/workflows/translation_check.yml (commit 7e3be47).
The workflow runs on every PR and push to master/develop. It:
- Installs Qt6
lupdateand runs it against all 18.tsfiles with-locations none(suppresses line-number churn) - Runs
git diff translations/— if lupdate changed any file, strings have drifted ci/check_translations.pyclassifies the diff: newtype="unfinished"stubs (newtr()calls needing translation) vstype="obsolete"entries (removedtr()calls), and prints actionable fix instructions
Using git diff rather than inspecting raw file content means the check is sensitive only to new drift, not pre-existing empty entries — so CI passes today and only fails when someone adds/removes a tr() call without updating translations.
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_Material, "Materials")); | ||
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_Material_NoMass, "No Mass Materials")); | ||
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_Material_AirGap, "Air Gap Materials")); | ||
| auto tr = [](const char* s) { return QCoreApplication::translate("openstudio::MaterialsView", s).toStdString(); }; |
There was a problem hiding this comment.
Can we just change the return type to std::vector<std::pair<IddObjectType, QString>> and use the normal tr? I don't like shadowing it with this local definition.
There was a problem hiding this comment.
Done — return type changed to std::vector<std::pair<IddObjectType, QString>> with direct tr() calls.
| {IddObjectType::OS_Schedule_Constant, "Schedule Constant"}, | ||
| {IddObjectType::OS_Schedule_Compact, "Schedule Compact"}, | ||
| {IddObjectType::OS_Schedule_File, "Schedule File"}, | ||
| {IddObjectType::OS_Schedule_Constant, QCoreApplication::translate("ScheduleOthersView", "Schedule Constant").toStdString()}, |
There was a problem hiding this comment.
Let's try changing the return type to std::vector<std::pair<IddObjectType, QString>> in these situations
There was a problem hiding this comment.
Done — return type changed to std::vector<std::pair<IddObjectType, QString>> with direct tr() calls.
| const QStringList monthNames = {tr("January"), tr("February"), tr("March"), tr("April"), | ||
| tr("May"), tr("June"), tr("July"), tr("August"), | ||
| tr("September"), tr("October"), tr("November"), tr("December")}; | ||
| m_monthLabel->setText((month >= 1 && month <= 12) ? monthNames[month - 1] : QString::fromStdString(monthOfYear(month).valueName())); |
There was a problem hiding this comment.
| const QStringList monthNames = {tr("January"), tr("February"), tr("March"), tr("April"), | |
| tr("May"), tr("June"), tr("July"), tr("August"), | |
| tr("September"), tr("October"), tr("November"), tr("December")}; | |
| m_monthLabel->setText((month >= 1 && month <= 12) ? monthNames[month - 1] : QString::fromStdString(monthOfYear(month).valueName())); | |
| const QStringList monthNames = {"January", "February", "March", "April", | |
| "May"), "June"), "July", "August", | |
| "September", "October", "November", "December"}; | |
| m_monthLabel->setText((month >= 1 && month <= 12) ? tr(monthNames[month - 1]) : QString::fromStdString(monthOfYear(month).valueName())); |
Would this be better? I don't really know?
There was a problem hiding this comment.
The current implementation already uses the correct pattern: const QStringList monthNames = {tr("January"), ...} with each string individually wrapped, so lupdate can extract them. The alternative (tr(variable)) wouldn't be extractable by lupdate. Leaving as-is.
| #include <QCheckBox> | ||
| #include <QCoreApplication> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::SpacesInteriorPartitionsGridController", s) |
There was a problem hiding this comment.
Let's not use defines, just use the normal tr pattern, we need to keep consistent to maintain this
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #define SELECTED "All" | ||
| #define DISPLAYNAME "Display Name" | ||
| #define CADOBJECTID "CAD Object ID" | ||
| #define NAME TR("Space Name") |
There was a problem hiding this comment.
| #define NAME TR("Space Name") | |
| #define NAME tr("Space Name") |
Can we use the normal tr here and remove define TR(s) above?
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #include <QCheckBox> | ||
| #include <QCoreApplication> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::SpacesShadingGridController", s) |
There was a problem hiding this comment.
Let's avoid this if we can
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #include <QCheckBox> | ||
| #include <QCoreApplication> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::SpacesSpacesGridController", s) |
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #include <QCheckBox> | ||
| #include <QCoreApplication> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::SpacesSubsurfacesGridController", s) |
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #include <QLabel> | ||
| #include <QLineEdit> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::SpacesSubtabGridView", s) |
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #include <QCheckBox> | ||
| #include <QCoreApplication> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::SpacesSurfacesGridController", s) |
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
|
|
||
| #define NAME "Space Type Name" | ||
| #define SELECTED "All" | ||
| #define TR(s) QCoreApplication::translate("openstudio::SpaceTypesGridController", s) |
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #include <QSettings> | ||
| #include <QTimer> | ||
|
|
||
| #define TR(s) QCoreApplication::translate("openstudio::ThermalZonesGridController", s) |
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| } | ||
|
|
||
| // i18n translation support | ||
| var _translations = { |
There was a problem hiding this comment.
Not sure we want to do this
There was a problem hiding this comment.
Reverted. We'll implement the proper Qt approach (injecting locale via QWebEnginePage::runJavaScript() and moving the strings into the .ts file) in a follow-up PR.
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_Construction, "Constructions")); | ||
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_Construction_AirBoundary, "Air Boundary Constructions")); | ||
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_Construction_InternalSource, "Internal Source Constructions")); | ||
| auto tr = [](const char* s) { return QCoreApplication::translate("openstudio::ConstructionsView", s).toStdString(); }; |
There was a problem hiding this comment.
Let's change return type to std::vector<std::pair<IddObjectType, QString>> to avoid re-defining tr
There was a problem hiding this comment.
Done — return type changed to std::vector<std::pair<IddObjectType, QString>> with direct tr() calls.
| #define SELECTED "All" | ||
| #define DISPLAYNAME "Display Name" | ||
| #define CADOBJECTID "CAD Object ID" | ||
| #define TR(s) QCoreApplication::translate("openstudio::FacilityExteriorEquipmentGridController", s) |
There was a problem hiding this comment.
Let's avoid defining a TR macro, just do #define NAME tr("Name") instead
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #define SELECTED "All" | ||
| #define DISPLAYNAME "Display Name" | ||
| #define CADOBJECTID "CAD Object ID" | ||
| #define TR(s) QCoreApplication::translate("openstudio::FacilityShadingGridController", s) |
There was a problem hiding this comment.
Let's avoid this if we can
There was a problem hiding this comment.
Done — all #define MACRO tr(...) patterns removed from all 13 grid view files. Column header strings are now inline tr() calls.
| #define SELECTED "All" | ||
| #define DISPLAYNAME "Display Name" | ||
| #define CADOBJECTID "CAD Object ID" | ||
| #define TR(s) QCoreApplication::translate("openstudio::FacilityStoriesGridController", s) |
There was a problem hiding this comment.
Let's avoid this if we can
There was a problem hiding this comment.
Done — replaced with inline tr() calls throughout (same fix applied to all 13 grid view files).
| m_view->setPage(m_page); // note, view does not take ownership of page | ||
|
|
||
| { | ||
| QWebEngineScript langScript; |
There was a problem hiding this comment.
I wonder if there is a more canonical way do this.
There was a problem hiding this comment.
This is the standard Qt QWebEngineScript::DocumentCreation injection point — it guarantees window.osLanguage is set before any JavaScript on the page runs. QWebEnginePage::runJavaScript() runs after page load and would race with the page's own initialization. The QWebEngineScript approach is recommended in the Qt docs for exactly this use case. Happy to discuss alternatives if you have a specific pattern in mind.
|
|
||
| // Inject locale so geometry_preview.html can select translations | ||
| { | ||
| QWebEngineScript langScript; |
There was a problem hiding this comment.
I wonder if there is a more canonical way to do this?
There was a problem hiding this comment.
Same as the EditorWebView above — QWebEngineScript::DocumentCreation is the standard Qt injection point that guarantees the variable is available before page JavaScript runs. Happy to discuss if you have a preferred alternative.
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_InternalMass_Definition, "Internal Mass Definitions")); | ||
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_WaterUse_Equipment_Definition, "Water Use Equipment Definitions")); | ||
| result.push_back(std::make_pair<IddObjectType, std::string>(IddObjectType::OS_HotWaterEquipment_Definition, "Hot Water Equipment Definitions")); | ||
| auto tr = [](const char* s) { return QCoreApplication::translate("openstudio::LoadsView", s).toStdString(); }; |
There was a problem hiding this comment.
Change return type to avoid re-defining tr
There was a problem hiding this comment.
Done (commit 05a157e) — return type is now std::vector<std::pair<IddObjectType, QString>> with direct tr() calls. The auto tr lambda is gone.
| myModelList->addModelObjectType(IddObjectType::OS_Construction_AirBoundary, "Air Boundary Constructions"); | ||
| myModelList->addModelObjectType(IddObjectType::OS_Construction, "Constructions"); | ||
| myModelList->addModelObjectCategoryPlaceholder("Constructions"); | ||
| myModelList->addModelObjectType(IddObjectType::OS_Construction_WindowDataFile, tr("Window Data File Constructions").toStdString()); |
There was a problem hiding this comment.
This is fine, could also change addModelObjectType to take a QString
There was a problem hiding this comment.
Done — addModelObjectType already takes const QString&, so tr() is passed directly. The .toStdString() conversion is gone.
|
|
||
| public: | ||
| explicit OSDropZone(OSVectorController* vectorController, const QString& text = "Drag From Library", const QSize& size = QSize(0, 0), | ||
| explicit OSDropZone(OSVectorController* vectorController, const QString& text = QString(), const QSize& size = QSize(0, 0), |
There was a problem hiding this comment.
Yes, intentional. tr("Drag From Library") can't be used as a default parameter (default args are evaluated at the call site without a QObject context). The constructor already handles the empty case: m_text(text.isEmpty() ? tr("Drag From Library") : text), so callers using the default still see the translated text. Callers that pass an explicit label are unaffected.
| // used on column headers, and other grid widgets | ||
|
|
||
| #define SELECTED "All" | ||
| #define TR_CASE(s) QCoreApplication::translate("openstudio::RefrigerationCaseGridController", s) |
There was a problem hiding this comment.
Let's avoid adding this TR_CASE macro
There was a problem hiding this comment.
Done — the TR_CASE macro has been removed. The refrigeration grid view column headers remain as plain #define string macros for now; that view is complex and was not in scope for this PR's tr() sweep.
|
|
||
| static void populateFrequencyComboBox(QComboBox* combo) { | ||
| // Uses the same context as VariablesList::tr() so existing translations apply. | ||
| const auto tr = [](const char* s) { |
There was a problem hiding this comment.
Let's avoid redefining tr
There was a problem hiding this comment.
Fixed (commit 05a157e) — removed the lambda. populateFrequencyComboBox is a free function (not a class method), so tr() isn't available. The calls now use QCoreApplication::translate(ctx, ...) with a named ctx constant directly at each call site.
There was a problem hiding this comment.
This tab will require extra testing
There was a problem hiding this comment.
Acknowledged — the Variables tab was significantly refactored (removed the old bind()-based reporting frequency selection, replaced with a manually populated QComboBox and indexChanged slot). Happy to describe the new flow or add test coverage if that would help review confidence.
|
|
||
| auto* header = new DarkGradientHeader(); | ||
| header->label->setText(item->name()); | ||
| header->label->setText(QCoreApplication::translate("TaxonomyCategories", item->name().toUtf8().constData())); |
There was a problem hiding this comment.
The QCoreApplication::translate("TaxonomyCategories", ...) is required here because the translations are stored under the "TaxonomyCategories" context (not the class name). Even though LibraryTypeItemDelegate has Q_OBJECT, using plain tr() would resolve to context "openstudio::LibraryTypeItemDelegate" — a different bucket. Moving to tr() would require renaming all those context entries in every .ts file, which is out of scope here. We could add a Q_DECLARE_TR_FUNCTIONS(TaxonomyCategories) but that conflicts with the existing Q_OBJECT. Current verbosity is the correct trade-off.
| void OSGridController::setCategoriesAndFields() { | ||
| std::vector<QString> fields; | ||
| std::pair<QString, std::vector<QString>> categoryAndFields = std::make_pair(QString("Custom"), fields); | ||
| std::pair<QString, std::vector<QString>> categoryAndFields = std::make_pair(QCoreApplication::translate("openstudio::OSGridController", "Custom"), fields); |
There was a problem hiding this comment.
We can just use tr if we add Q_DECLARE_TR_FUNCTIONS to the class right?
There was a problem hiding this comment.
Done (commit 05a157e) — OSGridController already has Q_OBJECT and is in the openstudio namespace, so tr("Custom") resolves to context "openstudio::OSGridController" which matches the .ts file entries. All three QCoreApplication::translate(...) calls replaced with tr().
|
This looks pretty good @Ski90Moo, thanks! I found a few anti-patterns I'd like to avoid, hopefully you can address those. I am also not sure if I like the approach for the html files, maybe we skip those for now? I made an internal PR at #874 which seems to complete the build CI actions, maybe those are failing here because this is an external PR? In any case, you can fix the cppcheck and Clang Format failures on your PR. |
|
One last question, did you get Claude to actually do the string translations, or just to add the |
…penstudiocoalition#680) - Expand language support from Spanish proof-of-concept to 18 languages: Arabic, Catalan, German, Greek, Persian, French, Hebrew, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Turkish, Vietnamese, Simplified Chinese (new), plus Spanish - Add Portuguese, Korean, Turkish, Indonesian to Preferences > Language menu with proper QAction wiring in MainMenu.cpp/.hpp - Fix RTL layout direction: setLayoutDirection(RightToLeft) for ar/fa/he, reset to LeftToRight when switching away from RTL languages - Enable Arabic in Language menu (was commented out) - Translate ~7,000 strings per language via Claude Haiku Batches API: UI strings, IDD field names, and OutputVariable display names - Fix apply_translations counter-shift bug: use same regex pattern as extract_unfinished so XML comments and <location/> tags don't cause wrong translations to be assigned to wrong source strings - Escape HTML entities (bare <, >, & and named entities like á) in all translation files to produce valid XML for lrelease - Add batch translation scripts: translate_all_languages.py, translate_new_languages.py, retranslate_stubborn.py, recover_batches.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oalition#680) - Wrap hardcoded strings in GeometryEditorView.cpp (Debug button) and GeometryPreviewView.cpp (Geometry Diagnostics checkbox tooltip) with tr() - Remove UTF-8 BOM from 27 inspector view .cpp files introduced by editor - Run clang-format on all modified C++ files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Translation_ts.IddCoverageForAllFields GTest that loops over every IddObject in the OpenStudio IDD (via IddFactory) and verifies each field name has a corresponding source entry in the IDD translation context of OpenStudioApp_es.ts. When the SDK adds new IDD objects/fields and the .ts file is not updated, the test fails with a list of missing field names. The fix is to run add_idd_skeleton.py then translate_skeleton.py to restore coverage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…diocoalition#680) Extend IddCoverageAllLanguages (renamed from IddCoverageForAllFields) to iterate every OpenStudioApp_*.ts file in the translations directory rather than only Spanish. For each language file the test independently extracts the IDD context source strings and reports any field names missing from that specific file, so drift after an SDK update is caught per-language. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion#680) Adds translation_check.yml workflow that runs on every PR and push to master/develop. After running lupdate6 with -locations none: - git diff detects if any .ts file changed — meaning strings were added to or removed from the C++ source without the translation files being updated. - ci/check_translations.py classifies the diff: new type="unfinished" stubs (new tr() calls needing translation) vs type="obsolete" entries (tr() calls removed from source). Reports actionable fix instructions for each case and exits 1 so CI fails. If the diff is empty, all .ts files are in sync and CI passes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…penstudiocoalition#680) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
All addressed:
|
|
Yes — Claude did both the Languages added: Arabic, Catalan, German, Greek, Persian/Farsi, French, Hebrew, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Spanish, Turkish, Vietnamese, Simplified Chinese. |
…th tr() - OSGridController: use tr() directly (class has Q_OBJECT, context matches) - VariablesTabView: remove translate lambda, use QCoreApplication::translate with a named ctx string to avoid redefining tr in free function scope Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wraps user-facing string literals with tr() across all major tabs and adds a complete Spanish (es) translation file as proof of concept for multi-language support, addressing issue #680.
Tabs covered: Site/Weather Data, Construction Sets, Materials, Schedules, Thermal Zones, Space Types, Loads, Facility, HVAC Systems, Inspector panel IDD fields, Output Variables (1051 names), Simulation Settings, Measures, Run Simulation, and Results Summary.
Key patterns established:
translations/OpenStudioApp_es.ts grows from ~200 to 3017 entries.
New languages only require a new .ts file with no further C++ changes.
The exception is the Geometry tab HTML files (geometry_editor_start.html and geometry_preview.html) each contain a hardcoded JavaScript dictionary keyed by locale code. We could:
Option A — Keep the Geometry tab HTML as an exception requiring separate edits.
Option B — Improve the architecture (better): Have the C++ side inject the locale into the HTML page at load time via QWebEnginePage::runJavaScript(), and move the translations out of the HTML into the .ts file.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com