diff --git a/special-pages/package.json b/special-pages/package.json index f49460067..c73fe38b5 100644 --- a/special-pages/package.json +++ b/special-pages/package.json @@ -6,7 +6,7 @@ "main": "index.js", "type": "module", "scripts": { - "prebuild": "node types.mjs", + "prebuild": "node types.mjs && node translations.mjs", "build": "node index.mjs", "build.dev": "npm run build -- --env development", "test-unit": "node --test unit-test/translations.mjs pages/duckplayer/unit-tests/embed-settings.mjs", @@ -20,7 +20,7 @@ "test.headed": "npm run test-int -- --headed", "test.ui": "npm run test-int -- --ui", "serve": "http-server -c-1 --port 3210 ../build/integration/pages", - "watch": "chokidar pages shared --initial -c 'npm run build.dev'" + "watch": "chokidar pages shared --initial -c 'npm run build.dev' --ignore 'pages/**/locales'" }, "license": "ISC", "devDependencies": { diff --git a/special-pages/pages/new-tab/app/favorites/components/Favorites.js b/special-pages/pages/new-tab/app/favorites/components/Favorites.js index e2e4169f0..5f267e933 100644 --- a/special-pages/pages/new-tab/app/favorites/components/Favorites.js +++ b/special-pages/pages/new-tab/app/favorites/components/Favorites.js @@ -6,7 +6,7 @@ import cn from 'classnames'; import styles from './Favorites.module.css'; import { Placeholder, PlusIconMemo, TileMemo } from './Tile.js'; import { ShowHideButton } from '../../components/ShowHideButton.jsx'; -import { useTypedTranslation } from '../../types.js'; +import { useTypedTranslationWith } from '../../types.js'; import { usePlatformName } from '../../settings.provider.js'; import { useDropzoneSafeArea } from '../../dropzone.js'; @@ -31,7 +31,7 @@ export const FavoritesMemo = memo(Favorites); */ export function Favorites({ gridRef, favorites, expansion, toggle, openContextMenu, openFavorite, add }) { const platformName = usePlatformName(); - const { t } = useTypedTranslation(); + const { t } = useTypedTranslationWith(/** @type {import('../strings.json')} */ ({})); const safeArea = useDropzoneSafeArea(); const ROW_CAPACITY = 6; diff --git a/special-pages/pages/new-tab/app/favorites/components/FavoritesCustomized.js b/special-pages/pages/new-tab/app/favorites/components/FavoritesCustomized.js index bdea5fc02..15fe22fb2 100644 --- a/special-pages/pages/new-tab/app/favorites/components/FavoritesCustomized.js +++ b/special-pages/pages/new-tab/app/favorites/components/FavoritesCustomized.js @@ -1,7 +1,7 @@ import { h } from 'preact'; import { useContext } from 'preact/hooks'; -import { useTelemetry, useTypedTranslation } from '../../types.js'; +import { useTelemetry, useTypedTranslationWith } from '../../types.js'; import { useVisibility } from '../../widget-list/widget-config.provider.js'; import { useCustomizer } from '../../customizer/components/Customizer.js'; @@ -38,7 +38,7 @@ export function FavoritesConsumer() { * Render the favorites widget, with integration into the page customizer */ export function FavoritesCustomized() { - const { t } = useTypedTranslation(); + const { t } = useTypedTranslationWith(/** @type {import("../strings.json")} */ ({})); const { id, visibility, toggle, index } = useVisibility(); // register with the visibility menu diff --git a/special-pages/pages/new-tab/app/favorites/strings.json b/special-pages/pages/new-tab/app/favorites/strings.json new file mode 100644 index 000000000..b2589c25c --- /dev/null +++ b/special-pages/pages/new-tab/app/favorites/strings.json @@ -0,0 +1,14 @@ +{ + "favorites_show_less": { + "title": "Show less", + "note": "Button label to display fewer items" + }, + "favorites_show_more": { + "title": "Show more ({count} remaining)", + "note": "Button text to show hidden items. {count} will be replaced with the number of remaining favorite items to show, including the parentheses. Example: 'Show more (18 remaining)'" + }, + "favorites_menu_title": { + "title": "Favorites", + "note": "Used as a label in a customization menu" + } +} diff --git a/special-pages/pages/new-tab/app/index.js b/special-pages/pages/new-tab/app/index.js index 83abc72dd..049a93425 100644 --- a/special-pages/pages/new-tab/app/index.js +++ b/special-pages/pages/new-tab/app/index.js @@ -7,7 +7,7 @@ import { SettingsProvider } from './settings.provider.js'; import { InitialSetupContext, MessagingContext, TelemetryContext } from './types'; import { TranslationProvider } from '../../../shared/components/TranslationsProvider.js'; import { WidgetConfigService } from './widget-list/widget-config.service.js'; -import enStrings from '../src/locales/en/newtab.json'; +import enStrings from '../src/locales/en/new-tab.json'; import { WidgetConfigProvider } from './widget-list/widget-config.provider.js'; import { Settings } from './settings.js'; import { Components } from './components/Components.jsx'; diff --git a/special-pages/pages/new-tab/src/js/mock-transport.js b/special-pages/pages/new-tab/app/mock-transport.js similarity index 91% rename from special-pages/pages/new-tab/src/js/mock-transport.js rename to special-pages/pages/new-tab/app/mock-transport.js index 68be9c0b6..c4dd80d9b 100644 --- a/special-pages/pages/new-tab/src/js/mock-transport.js +++ b/special-pages/pages/new-tab/app/mock-transport.js @@ -1,24 +1,24 @@ import { TestTransportConfig } from '@duckduckgo/messaging'; -import { stats } from '../../app/privacy-stats/mocks/stats.js'; -import { rmfDataExamples } from '../../app/remote-messaging-framework/mocks/rmf.data.js'; -import { favorites, gen } from '../../app/favorites/mocks/favorites.data.js'; -import { updateNotificationExamples } from '../../app/update-notification/mocks/update-notification.data.js'; -import { variants as nextSteps } from '../../app/next-steps/nextsteps.data.js'; +import { stats } from './privacy-stats/mocks/stats.js'; +import { rmfDataExamples } from './remote-messaging-framework/mocks/rmf.data.js'; +import { favorites, gen } from './favorites/mocks/favorites.data.js'; +import { updateNotificationExamples } from './update-notification/mocks/update-notification.data.js'; +import { variants as nextSteps } from './next-steps/nextsteps.data.js'; /** - * @typedef {import('../../../../types/new-tab').Favorite} Favorite - * @typedef {import('../../../../types/new-tab').FavoritesData} FavoritesData - * @typedef {import('../../../../types/new-tab').FavoritesConfig} FavoritesConfig - * @typedef {import('../../../../types/new-tab').StatsConfig} StatsConfig - * @typedef {import('../../../../types/new-tab').NextStepsConfig} NextStepsConfig - * @typedef {import('../../../../types/new-tab').NextStepsCards} NextStepsCards - * @typedef {import('../../../../types/new-tab').NextStepsData} NextStepsData - * @typedef {import('../../../../types/new-tab').UpdateNotificationData} UpdateNotificationData - * @typedef {import('../../../../types/new-tab.js').NewTabMessages['subscriptions']['subscriptionEvent']} SubscriptionNames + * @typedef {import('../../../types/new-tab').Favorite} Favorite + * @typedef {import('../../../types/new-tab').FavoritesData} FavoritesData + * @typedef {import('../../../types/new-tab').FavoritesConfig} FavoritesConfig + * @typedef {import('../../../types/new-tab').StatsConfig} StatsConfig + * @typedef {import('../../../types/new-tab').NextStepsConfig} NextStepsConfig + * @typedef {import('../../../types/new-tab').NextStepsCards} NextStepsCards + * @typedef {import('../../../types/new-tab').NextStepsData} NextStepsData + * @typedef {import('../../../types/new-tab').UpdateNotificationData} UpdateNotificationData + * @typedef {import('../../../types/new-tab').NewTabMessages['subscriptions']['subscriptionEvent']} SubscriptionNames */ -const VERSION_PREFIX = '__ntp_28__.'; +const VERSION_PREFIX = '__ntp_29__.'; const url = new URL(window.location.href); export function mockTransport() { @@ -75,7 +75,7 @@ export function mockTransport() { function clearRmf() { const listeners = rmfSubscriptions.get('rmf_onDataUpdate') || []; - /** @type {import('../../../../types/new-tab.js').RMFData} */ + /** @type {import('../../../types/new-tab.ts').RMFData} */ const message = { content: undefined }; for (const listener of listeners) { listener(message); @@ -85,7 +85,7 @@ export function mockTransport() { return new TestTransportConfig({ notify(_msg) { window.__playwright_01?.mocks?.outgoing?.push?.({ payload: structuredClone(_msg) }); - /** @type {import('../../../../types/new-tab.js').NewTabMessages['notifications']} */ + /** @type {import('../../../types/new-tab.ts').NewTabMessages['notifications']} */ const msg = /** @type {any} */ (_msg); switch (msg.method) { case 'widgets_setConfig': { @@ -154,7 +154,7 @@ export function mockTransport() { }, subscribe(_msg, cb) { window.__playwright_01?.mocks?.outgoing?.push?.({ payload: structuredClone(_msg) }); - /** @type {import('../../../../types/new-tab.js').NewTabMessages['subscriptions']['subscriptionEvent']} */ + /** @type {import('../../../types/new-tab.ts').NewTabMessages['subscriptions']['subscriptionEvent']} */ const sub = /** @type {any} */ (_msg.subscriptionName); switch (sub) { case 'widgets_onConfigUpdated': { @@ -303,7 +303,7 @@ export function mockTransport() { // eslint-ignore-next-line require-await request(_msg) { window.__playwright_01?.mocks?.outgoing?.push?.({ payload: structuredClone(_msg) }); - /** @type {import('../../../../types/new-tab.js').NewTabMessages['requests']} */ + /** @type {import('../../../types/new-tab.ts').NewTabMessages['requests']} */ const msg = /** @type {any} */ (_msg); switch (msg.method) { case 'stats_getData': { @@ -353,7 +353,7 @@ export function mockTransport() { return Promise.resolve(data); } case 'rmf_getData': { - /** @type {import('../../../../types/new-tab.js').RMFData} */ + /** @type {import('../../../types/new-tab.ts').RMFData} */ let message = { content: undefined }; const rmfParam = url.searchParams.get('rmf'); @@ -414,7 +414,7 @@ export function mockTransport() { updateNotification = updateNotificationExamples.populated; } - /** @type {import('../../../../types/new-tab.js').InitialSetupResponse} */ + /** @type {import('../../../types/new-tab.ts').InitialSetupResponse} */ const initial = { widgets: widgetsFromStorage, widgetConfigs: widgetConfigFromStorage, diff --git a/special-pages/pages/new-tab/app/next-steps/components/NextStepsCard.js b/special-pages/pages/new-tab/app/next-steps/components/NextStepsCard.js index e4eab3cf0..a769ada96 100644 --- a/special-pages/pages/new-tab/app/next-steps/components/NextStepsCard.js +++ b/special-pages/pages/new-tab/app/next-steps/components/NextStepsCard.js @@ -2,7 +2,7 @@ import { h } from 'preact'; import styles from './NextSteps.module.css'; import { DismissButton } from '../../components/DismissButton'; import { variants } from '../nextsteps.data'; -import { useTypedTranslation } from '../../types'; +import { useTypedTranslationWith } from '../../types'; /** * @param {object} props @@ -12,7 +12,7 @@ import { useTypedTranslation } from '../../types'; */ export function NextStepsCard({ type, dismiss, action }) { - const { t } = useTypedTranslation(); + const { t } = useTypedTranslationWith(/** @type {import("../strings.json")} */ ({})); const message = variants[type]?.(t); return (
{t('trackerStatsNoRecent')}
} + {none &&{t('stats_noRecent')}
} {some &&{alltimeTitle}
} {recent > 0 && ( @@ -118,15 +113,13 @@ export function Heading({ expansion, trackerCompanies, totalCount, onToggle, but 'aria-pressed': expansion === 'expanded', }} onClick={onToggle} - text={expansion === 'expanded' ? t('trackerStatsHideLabel') : t('trackerStatsToggleLabel')} + text={expansion === 'expanded' ? t('stats_hideLabel') : t('stats_toggleLabel')} shape="round" /> )} -- {recent === 0 && t('trackerStatsNoActivity')} - {recent > 0 && recentTitle} -
+ {recent === 0 &&{t('stats_noActivity')}
} + {recent > 0 &&{t('stats_feedCountBlockedPeriod')}
}