fix(plugins): initialize current language on plugin mount

Fixed i18n to fetch and use the current language when the plugin
initializes, instead of always defaulting to English. Now the plugin
will show the correct language immediately on first load, and also
update reactively when the language changes.

Changes:
- Added getCurrentLanguage() call in useTranslate initialization
- Changed onMount to createEffect for reactive language loading
- Updated both App.tsx and ProcrastinationInfo.tsx to use createEffect
- Applied fixes to both procrastination-buster and boilerplate
- Added debug logging to troubleshoot language change events

This fixes the issue where plugins always started in English regardless
of the user's selected language.
This commit is contained in:
Johannes Millan 2026-01-16 22:07:58 +01:00
parent fb05e7be3e
commit 9be6bf7c27
5 changed files with 82 additions and 41 deletions

View file

@ -57,6 +57,18 @@ const sendMessage = async (type: string, payload?: any): Promise<any> => {
export function useTranslate() {
const [currentLanguage, setCurrentLanguage] = createSignal<string>('en');
// Initialize current language on mount
createEffect(() => {
(async () => {
try {
const lang = await sendMessage('getCurrentLanguage');
setCurrentLanguage(lang);
} catch (error) {
console.error('[useTranslate] Failed to get current language:', error);
}
})();
});
// Listen for language change events
createEffect(() => {
const handleLanguageChange = (event: MessageEvent) => {

View file

@ -28,20 +28,25 @@ const App: Component = () => {
const [actionButton, setActionButton] = createSignal('');
const [actionButtonTitle, setActionButtonTitle] = createSignal('');
// Load translations
onMount(async () => {
// Load UI translations
setHomeTitle(await t('HOME.TITLE'));
setHomeSubtitle(await t('HOME.SUBTITLE'));
setLearnMoreButton(await t('HOME.LEARN_MORE_BUTTON'));
setBackButton(await t('NAVIGATION.BACK'));
setStrategiesTitle(await t('STRATEGIES.TITLE'));
setActionButton(await t('STRATEGIES.ACTION_BUTTON'));
setActionButtonTitle(await t('STRATEGIES.ACTION_BUTTON_TITLE'));
// Load translations and reload when language changes
createEffect(() => {
// Watch for language changes by accessing the currentLanguage signal
const lang = t.currentLanguage();
// Load procrastination types with translations
const types = await getProcrastinationTypes(t);
setProcrastinationTypes(types);
// Load all translations (this runs on mount AND when language changes)
(async () => {
setHomeTitle(await t('HOME.TITLE'));
setHomeSubtitle(await t('HOME.SUBTITLE'));
setLearnMoreButton(await t('HOME.LEARN_MORE_BUTTON'));
setBackButton(await t('NAVIGATION.BACK'));
setStrategiesTitle(await t('STRATEGIES.TITLE'));
setActionButton(await t('STRATEGIES.ACTION_BUTTON'));
setActionButtonTitle(await t('STRATEGIES.ACTION_BUTTON_TITLE'));
// Load procrastination types with translations
const types = await getProcrastinationTypes(t);
setProcrastinationTypes(types);
})();
});
const handleSelectType = (type: ProcrastinationType) => {

View file

@ -1,4 +1,4 @@
import { Component, createSignal, onMount } from 'solid-js';
import { Component, createSignal, createEffect } from 'solid-js';
import { useTranslate } from './utils/useTranslate';
import './App.css';
@ -36,33 +36,39 @@ export const ProcrastinationInfo: Component<ProcrastinationInfoProps> = (props)
const [triggersText, setTriggersText] = createSignal('');
const [backToWork, setBackToWork] = createSignal('');
// Load translations
onMount(async () => {
setTitle(await t('INFO.TITLE'));
setIntro(await t('INFO.INTRO'));
setCycleTitle(await t('INFO.CYCLE_TITLE'));
setCycleIntro(await t('INFO.CYCLE_INTRO'));
setCycleStep1(await t('INFO.CYCLE_STEP_1'));
setCycleStep2(await t('INFO.CYCLE_STEP_2'));
setCycleStep3(await t('INFO.CYCLE_STEP_3'));
setCycleStep4(await t('INFO.CYCLE_STEP_4'));
setBreakingTitle(await t('INFO.BREAKING_TITLE'));
setBreakingIntro(await t('INFO.BREAKING_INTRO'));
setBreakingQ1(await t('INFO.BREAKING_Q1'));
setBreakingQ2(await t('INFO.BREAKING_Q2'));
setBreakingQ3(await t('INFO.BREAKING_Q3'));
setStrategiesTitle(await t('INFO.STRATEGIES_TITLE'));
setStrategyStartSmall(await t('INFO.STRATEGY_START_SMALL'));
setStrategyStartSmallDesc(await t('INFO.STRATEGY_START_SMALL_DESC'));
setStrategyTimebox(await t('INFO.STRATEGY_TIMEBOX'));
setStrategyTimeboxDesc(await t('INFO.STRATEGY_TIMEBOX_DESC'));
setStrategyReframe(await t('INFO.STRATEGY_REFRAME'));
setStrategyReframeDesc(await t('INFO.STRATEGY_REFRAME_DESC'));
setStrategyCompassion(await t('INFO.STRATEGY_COMPASSION'));
setStrategyCompassionDesc(await t('INFO.STRATEGY_COMPASSION_DESC'));
setTriggersTitle(await t('INFO.TRIGGERS_TITLE'));
setTriggersText(await t('INFO.TRIGGERS_TEXT'));
setBackToWork(await t('INFO.BACK_TO_WORK'));
// Load translations and reload when language changes
createEffect(() => {
// Watch for language changes by accessing the currentLanguage signal
const lang = t.currentLanguage();
// Load all translations (this runs on mount AND when language changes)
(async () => {
setTitle(await t('INFO.TITLE'));
setIntro(await t('INFO.INTRO'));
setCycleTitle(await t('INFO.CYCLE_TITLE'));
setCycleIntro(await t('INFO.CYCLE_INTRO'));
setCycleStep1(await t('INFO.CYCLE_STEP_1'));
setCycleStep2(await t('INFO.CYCLE_STEP_2'));
setCycleStep3(await t('INFO.CYCLE_STEP_3'));
setCycleStep4(await t('INFO.CYCLE_STEP_4'));
setBreakingTitle(await t('INFO.BREAKING_TITLE'));
setBreakingIntro(await t('INFO.BREAKING_INTRO'));
setBreakingQ1(await t('INFO.BREAKING_Q1'));
setBreakingQ2(await t('INFO.BREAKING_Q2'));
setBreakingQ3(await t('INFO.BREAKING_Q3'));
setStrategiesTitle(await t('INFO.STRATEGIES_TITLE'));
setStrategyStartSmall(await t('INFO.STRATEGY_START_SMALL'));
setStrategyStartSmallDesc(await t('INFO.STRATEGY_START_SMALL_DESC'));
setStrategyTimebox(await t('INFO.STRATEGY_TIMEBOX'));
setStrategyTimeboxDesc(await t('INFO.STRATEGY_TIMEBOX_DESC'));
setStrategyReframe(await t('INFO.STRATEGY_REFRAME'));
setStrategyReframeDesc(await t('INFO.STRATEGY_REFRAME_DESC'));
setStrategyCompassion(await t('INFO.STRATEGY_COMPASSION'));
setStrategyCompassionDesc(await t('INFO.STRATEGY_COMPASSION_DESC'));
setTriggersTitle(await t('INFO.TRIGGERS_TITLE'));
setTriggersText(await t('INFO.TRIGGERS_TEXT'));
setBackToWork(await t('INFO.BACK_TO_WORK'));
})();
});
return (

View file

@ -25,9 +25,12 @@ if (plugin.onMessage) {
// Listen for language changes and notify iframe
plugin.registerHook('languageChange', (language: string) => {
plugin.log.info('[plugin.ts] Language changed to:', language);
// Notify the iframe about language change
const iframe = document.querySelector('iframe[data-plugin-iframe]');
plugin.log.info('[plugin.ts] Found iframe:', !!iframe);
if (iframe && (iframe as HTMLIFrameElement).contentWindow) {
plugin.log.info('[plugin.ts] Sending languageChanged message to iframe');
(iframe as HTMLIFrameElement).contentWindow!.postMessage(
{ type: 'languageChanged', language },
'*',

View file

@ -60,10 +60,25 @@ const sendMessage = async (type: string, payload?: any): Promise<any> => {
export function useTranslate() {
const [currentLanguage, setCurrentLanguage] = createSignal<string>('en');
// Initialize current language on mount
createEffect(() => {
(async () => {
try {
const lang = await sendMessage('getCurrentLanguage');
console.log('[useTranslate] Initial language:', lang);
setCurrentLanguage(lang);
} catch (error) {
console.error('[useTranslate] Failed to get current language:', error);
}
})();
});
// Listen for language change events
createEffect(() => {
const handleLanguageChange = (event: MessageEvent) => {
console.log('[useTranslate] Received event:', event.data.type);
if (event.data.type === 'languageChanged') {
console.log('[useTranslate] Language changed to:', event.data.language);
setCurrentLanguage(event.data.language);
}
};