feat(i18n): Implement runtime internationalization language support, changelog and bug fix #175
feat(i18n): Implement runtime internationalization language support, changelog and bug fix #175LeandroGazoli wants to merge 1 commit intosamueljun:masterfrom
Conversation
…age support - Added a runtime internationalization loader (`src/utils/i18n.js`) to load translations from `_locales/<lang>/messages.json`. - Introduced language selection in the Options page, allowing users to choose their preferred language (default: English). - Replaced static strings in HTML files with `__MSG_...__` placeholders for localization. - Updated JavaScript files to utilize the new `t()` helper for consistent translation handling. - Enhanced the Options UI to support dynamic language switching and persist user preferences in settings. - Included English and Portuguese translations in `_locales/en/messages.json` and `_locales/pt/messages.json`. - Updated `webpack.config.js` to copy the `_locales` folder into the distribution directory for translation loading. - Added a `CHANGELOG.md` to document notable changes and updates in the project.
There was a problem hiding this comment.
Pull request overview
This pull request implements comprehensive runtime internationalization (i18n) support for the Tomato Clock browser extension, enabling dynamic language switching between English and Portuguese. The implementation introduces a new i18n utility module that loads translations from _locales/<lang>/messages.json files, replaces static strings across all HTML files with __MSG_...__ placeholders, and adds a language selector to the Options page. Additionally, the PR improves cross-platform development workflow by adding a Node.js script to auto-detect Firefox installations on Windows, macOS, and Linux.
Changes:
- Added runtime i18n loader (
src/utils/i18n.js) with translation functions, language switching, and automatic message loading - Replaced static UI strings in HTML files and JavaScript with localized placeholders and runtime translation calls
- Added English and Portuguese translation files in
_locales/en/messages.jsonand_locales/pt/messages.json - Introduced cross-platform development script (
scripts/run-webext.js) with Firefox auto-detection and Windows compatibility fixes - Updated build configuration to copy locale files and added comprehensive changelog
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/i18n.js | New runtime i18n loader with message loading, translation functions, and storage listeners |
| src/utils/constants.js | Added LANGUAGE setting key, AVAILABLE_LANGUAGES constant, and removed hardcoded sound names |
| src/stats/stats.js | Wrapped constructor in async localization, added language change listeners for chart/daterangepicker updates |
| src/stats/stats.html | Replaced static strings with __MSG_...__ placeholders |
| src/panel/panel.js | Added async localization wrapper and options link handler |
| src/panel/panel.html | Replaced static strings with localization placeholders and added options button |
| src/options/options.js | Added language selector population, dynamic translation updates, and language change handler |
| src/options/options.html | Replaced static strings with placeholders and added language selector UI |
| src/offscreen/offscreen.js | Added localization initialization call |
| src/offscreen/offscreen.html | Replaced title with localization placeholder |
| src/manifest.json | Added default_locale and replaced static strings with __MSG_...__ placeholders |
| scripts/run-webext.js | New cross-platform script with Firefox auto-detection and Windows shell compatibility |
| webpack.config.js | Added copy rule for _locales folder to distribution directory |
| package.json | Updated version to 7.2.1 and modified start scripts to use new runner |
| _locales/en/messages.json | Complete English translation file with 92 message keys |
| _locales/pt/messages.json | Complete Portuguese translation file with 92 message keys |
| README.md | Added Windows-specific development instructions with FIREFOX_PATH setup |
| CHANGELOG.md | New changelog documenting all changes in version 7.2.1 |
Comments suppressed due to low confidence (2)
src/stats/stats.js:373
- The Stats constructor initializes asynchronously via
localizeHtmlPage().then(), but the daterangepicker initialization code (lines 303-373) that depends on the Stats instance runs synchronously immediately afternew Stats(). This creates a race condition where the daterangepicker may attempt to callstats.changeStatDates()before the Stats instance is fully initialized (DOM elements bound, event listeners set up, etc.).
Consider refactoring to ensure the Stats instance is fully initialized before the daterangepicker is set up, for example by having the Stats constructor return a promise or by moving the daterangepicker initialization inside the localizeHtmlPage().then() callback.
$(document).ready(() => {
const stats = new Stats();
// Date Picker
const momentLastWeek = moment().subtract(6, "days");
const momentToday = moment();
// Build localized ranges for daterangepicker
const rangeLabels = {
last7Days: t("range_last_7_days"),
thisWeek: t("range_this_week"),
lastWeek: t("range_last_week"),
last30Days: t("range_last_30_days"),
thisMonth: t("range_this_month"),
lastMonth: t("range_last_month"),
thisYear: t("range_this_year"),
lastYear: t("range_last_year"),
};
const ranges = {};
ranges[rangeLabels.last7Days] = [moment().subtract(6, "days"), moment()];
ranges[rangeLabels.thisWeek] = [
moment().startOf("week"),
moment().endOf("week"),
];
ranges[rangeLabels.lastWeek] = [
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
];
ranges[rangeLabels.last30Days] = [moment().subtract(29, "days"), moment()];
ranges[rangeLabels.thisMonth] = [
moment().startOf("month"),
moment().endOf("month"),
];
ranges[rangeLabels.lastMonth] = [
moment().subtract(1, "month").startOf("month"),
moment().subtract(1, "month").endOf("month"),
];
ranges[rangeLabels.thisYear] = [
moment().startOf("year"),
moment().endOf("year"),
];
ranges[rangeLabels.lastYear] = [
moment().subtract(1, "year").startOf("year"),
moment().subtract(1, "year").endOf("year"),
];
$('input[name="daterange"]').daterangepicker(
{
locale: {
format: t("dateFormat") || "dddd, MMMM Do YYYY",
},
dateLimit: {
months: 1,
},
startDate: momentLastWeek,
endDate: momentToday,
ranges,
},
(momentStartDate, momentEndDate, label) => {
const startDate = momentStartDate.toDate();
const endDate = momentEndDate.toDate();
const isRangeYear =
label === rangeLabels.thisYear || label === rangeLabels.lastYear;
const dateUnit = isRangeYear ? DATE_UNIT.MONTH : DATE_UNIT.DAY;
stats.changeStatDates(startDate, endDate, dateUnit);
},
);
});
src/stats/stats.js:143
- The event listener for
importStatsHiddenInputis set up on line 140-143, butthis.importStatsHiddenInputis only assigned inside thelocalizeHtmlPage().then()callback (line 31-33). This will cause a runtime error when the constructor executes becausethis.importStatsHiddenInputwill be undefined at the time the event listener is being added.
Move this event listener setup inside the localizeHtmlPage().then() callback after the DOM element is assigned, similar to how other event listeners are set up (lines 48-59).
this.importStatsHiddenInput.addEventListener(
"change",
this.handleImportStatsHiddenInputChange,
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
feat(i18n): Implement runtime internationalization with dynamic language support
src/utils/i18n.js) to load translations from_locales/<lang>/messages.json.__MSG_...__placeholders for localization.t()helper for consistent translation handling._locales/en/messages.jsonand_locales/pt/messages.json.webpack.config.jsto copy the_localesfolder into the distribution directory for translation loading.CHANGELOG.mdto document notable changes and updates in the project.This pull request introduces full runtime internationalization (i18n) support for the extension, enabling dynamic language switching between English and Portuguese. It replaces static UI strings with localized placeholders, adds a language selector to the Options page, and ensures translations are applied across all pages. The build and development scripts are updated for improved cross-platform compatibility, especially for Windows users.
Internationalization and Localization:
src/utils/i18n.js) and helper functions (t(),setLanguage(),applyTranslations()) for runtime translation without page reload.src/manifest.json,src/options/options.html, andsrc/offscreen/offscreen.htmlwith__MSG_...__placeholders for runtime localization. (F41a42bbR1, [1] [2] [3] [4] [5] [6] [7] [8]_locales/en/messages.jsonand_locales/pt/messages.jsonwith translations for all UI labels and sound names. [1] [2]Build and Development Workflow:
webpack.config.jsto copy the_localesfolder intodist/for proper loading byweb-ext.scripts/run-webext.jsfor cross-platformnpm start, auto-detecting Firefox installation and improving Windows/Chromium handling. [1] [2]README.mdwith Windows-specific instructions for settingFIREFOX_PATHand running the extension.Bug Fixes:
spawn EINVALerror on Windows when runningweb-extvianpxby using shell invocation in the runner. [1] [2]Documentation and Versioning:
CHANGELOG.mddocumenting all notable changes.7.2.1inpackage.json.