mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
fix(editor): Convert autocompleteUIValues to a getter and streamline i18n HMR import (no-changelog) (#19560)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable id-denylist */
|
||||
import { I18nClass, loadLanguage, i18nInstance } from './index';
|
||||
import type { LocaleMessages } from './types';
|
||||
|
||||
// Store original state for cleanup
|
||||
let originalLocale: string;
|
||||
@@ -16,7 +17,7 @@ describe(I18nClass, () => {
|
||||
minShort: 'm',
|
||||
hrsShort: 'h',
|
||||
},
|
||||
});
|
||||
} as unknown as LocaleMessages);
|
||||
originalLocale = i18nInstance.global.locale.value;
|
||||
originalHtmlLang = document.querySelector('html')?.getAttribute('lang') ?? 'en';
|
||||
});
|
||||
@@ -78,7 +79,7 @@ describe('loadLanguage', () => {
|
||||
const messages = {
|
||||
hello: 'Hallo',
|
||||
world: 'Welt',
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
const result = loadLanguage(locale, messages);
|
||||
|
||||
@@ -91,7 +92,7 @@ describe('loadLanguage', () => {
|
||||
|
||||
it('should set the HTML lang attribute when loading a language', () => {
|
||||
const locale = 'fr';
|
||||
const messages = { greeting: 'Bonjour' };
|
||||
const messages = { greeting: 'Bonjour' } as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -109,7 +110,7 @@ describe('loadLanguage', () => {
|
||||
currency: 'EUR',
|
||||
},
|
||||
},
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -122,8 +123,8 @@ describe('loadLanguage', () => {
|
||||
|
||||
it('should not reload a language if it has already been loaded', () => {
|
||||
const locale = 'es';
|
||||
const originalMessages = { hello: 'Hola' };
|
||||
const newMessages = { hello: 'Buenos días' };
|
||||
const originalMessages = { hello: 'Hola' } as unknown as LocaleMessages;
|
||||
const newMessages = { hello: 'Buenos días' } as unknown as LocaleMessages;
|
||||
|
||||
// Load the language for the first time
|
||||
loadLanguage(locale, originalMessages);
|
||||
@@ -144,7 +145,7 @@ describe('loadLanguage', () => {
|
||||
|
||||
it('should handle empty messages object', () => {
|
||||
const locale = 'it';
|
||||
const messages = {};
|
||||
const messages = {} as unknown as LocaleMessages;
|
||||
|
||||
const result = loadLanguage(locale, messages);
|
||||
|
||||
@@ -162,7 +163,7 @@ describe('loadLanguage', () => {
|
||||
},
|
||||
},
|
||||
simple: 'Simples',
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -187,7 +188,7 @@ describe('loadLanguage', () => {
|
||||
minimumFractionDigits: 2,
|
||||
},
|
||||
},
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -205,17 +206,17 @@ describe('loadLanguage', () => {
|
||||
const locale2 = 'de-switch';
|
||||
|
||||
// Load first language
|
||||
loadLanguage(locale1, { hello: 'Bonjour' });
|
||||
loadLanguage(locale1, { hello: 'Bonjour' } as unknown as LocaleMessages);
|
||||
expect(i18nInstance.global.locale.value).toBe(locale1);
|
||||
expect(i18nInstance.global.t('hello')).toBe('Bonjour');
|
||||
|
||||
// Load second language
|
||||
loadLanguage(locale2, { hello: 'Hallo' });
|
||||
loadLanguage(locale2, { hello: 'Hallo' } as unknown as LocaleMessages);
|
||||
expect(i18nInstance.global.locale.value).toBe(locale2);
|
||||
expect(i18nInstance.global.t('hello')).toBe('Hallo');
|
||||
|
||||
// Switch back to first language (should not reload messages)
|
||||
loadLanguage(locale1, { hello: 'Salut' }); // Different message
|
||||
loadLanguage(locale1, { hello: 'Salut' } as unknown as LocaleMessages); // Different message
|
||||
expect(i18nInstance.global.locale.value).toBe(locale1);
|
||||
expect(i18nInstance.global.t('hello')).toBe('Bonjour'); // Should be original message
|
||||
testLocales.add(locale1);
|
||||
@@ -224,7 +225,7 @@ describe('loadLanguage', () => {
|
||||
|
||||
it('should return the locale that was set', () => {
|
||||
const locale = 'nl';
|
||||
const messages = { test: 'test' };
|
||||
const messages = { test: 'test' } as unknown as LocaleMessages;
|
||||
|
||||
const result = loadLanguage(locale, messages);
|
||||
|
||||
@@ -239,7 +240,7 @@ describe('loadLanguage', () => {
|
||||
special: '特殊字符测试',
|
||||
emoji: '🚀 测试 🎉',
|
||||
mixed: 'Mixed 混合 content',
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -256,7 +257,7 @@ describe('loadLanguage', () => {
|
||||
defined: 'Valid message',
|
||||
undefined,
|
||||
null: null,
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -275,7 +276,7 @@ describe('loadLanguage', () => {
|
||||
nested: {
|
||||
list: ['a', 'b', 'c'],
|
||||
},
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -290,7 +291,7 @@ describe('loadLanguage', () => {
|
||||
const currentLocale = 'en';
|
||||
const messages = {
|
||||
newMessage: 'This is a new message',
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
// Ensure we're starting with English
|
||||
i18nInstance.global.locale.value = currentLocale;
|
||||
@@ -322,7 +323,7 @@ describe('loadLanguage', () => {
|
||||
maximumFractionDigits: 3,
|
||||
},
|
||||
},
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -347,7 +348,7 @@ describe('loadLanguage', () => {
|
||||
number: 42,
|
||||
zero: 0,
|
||||
string: 'actual string',
|
||||
};
|
||||
} as unknown as LocaleMessages;
|
||||
|
||||
loadLanguage(locale, messages);
|
||||
|
||||
@@ -369,10 +370,10 @@ describe('loadLanguage', () => {
|
||||
const locale1 = 'preserve-1';
|
||||
const locale2 = 'preserve-2';
|
||||
|
||||
loadLanguage(locale1, { test: 'test1' });
|
||||
loadLanguage(locale1, { test: 'test1' } as unknown as LocaleMessages);
|
||||
expect(html?.getAttribute('lang')).toBe(locale1);
|
||||
|
||||
loadLanguage(locale2, { test: 'test2' });
|
||||
loadLanguage(locale2, { test: 'test2' } as unknown as LocaleMessages);
|
||||
expect(html?.getAttribute('lang')).toBe(locale2);
|
||||
|
||||
// Restore original
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { INodeProperties, INodePropertyCollection, INodePropertyOptions } f
|
||||
import { ref } from 'vue';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
import type { BaseTextKey, INodeTranslationHeaders } from './types';
|
||||
import englishBaseText from './locales/en.json';
|
||||
import type { BaseTextKey, LocaleMessages, INodeTranslationHeaders } from './types';
|
||||
import {
|
||||
deriveMiddleKey,
|
||||
isNestedInCollectionLike,
|
||||
@@ -17,7 +18,7 @@ export const i18nInstance = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
messages: { en: {} },
|
||||
messages: { en: englishBaseText },
|
||||
warnHtmlMessage: false,
|
||||
});
|
||||
|
||||
@@ -402,7 +403,7 @@ export function setLanguage(locale: string) {
|
||||
return locale;
|
||||
}
|
||||
|
||||
export function loadLanguage(locale: string, messages: Record<string, unknown>) {
|
||||
export function loadLanguage(locale: string, messages: LocaleMessages) {
|
||||
if (loadedLanguages.includes(locale)) {
|
||||
return setLanguage(locale);
|
||||
}
|
||||
@@ -440,10 +441,8 @@ export function addNodeTranslation(
|
||||
* Dev/runtime helper to replace messages for a locale without import side-effects.
|
||||
* Used by editor UI HMR to apply updated translation JSON.
|
||||
*/
|
||||
export function updateLocaleMessages(locale: string, messages: Record<string, unknown>) {
|
||||
const { numberFormats, ...rest } = messages as Record<string, unknown> & {
|
||||
numberFormats?: Record<string, unknown>;
|
||||
};
|
||||
export function updateLocaleMessages(locale: string, messages: LocaleMessages) {
|
||||
const { numberFormats, ...rest } = messages;
|
||||
|
||||
i18nInstance.global.setLocaleMessage(locale, rest);
|
||||
if (numberFormats) i18nInstance.global.setNumberFormat(locale, numberFormats);
|
||||
|
||||
@@ -3,7 +3,9 @@ import type englishBaseText from './locales/en.json';
|
||||
export type GetBaseTextKey<T> = T extends `_${string}` ? never : T;
|
||||
|
||||
export type BaseTextKey = GetBaseTextKey<keyof typeof englishBaseText>;
|
||||
export type LocaleMessages = typeof englishBaseText;
|
||||
export type LocaleMessages = typeof englishBaseText & {
|
||||
numberFormats: { [key: string]: Intl.NumberFormatOptions };
|
||||
};
|
||||
|
||||
export interface INodeTranslationHeaders {
|
||||
data: {
|
||||
|
||||
@@ -109,8 +109,6 @@ watch(
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// Dev HMR for i18n is imported in main.ts before app mount
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'fake-indexeddb/auto';
|
||||
import { configure } from '@testing-library/vue';
|
||||
import 'core-js/proposals/set-methods-v2';
|
||||
import englishBaseText from '@n8n/i18n/locales/en.json';
|
||||
import { loadLanguage } from '@n8n/i18n';
|
||||
import { loadLanguage, type LocaleMessages } from '@n8n/i18n';
|
||||
import { APP_MODALS_ELEMENT_ID } from '@/constants';
|
||||
|
||||
// Avoid tests failing because of difference between local and GitHub actions timezone
|
||||
@@ -147,4 +147,4 @@ Object.defineProperty(HTMLElement.prototype, 'scrollTo', {
|
||||
value: vi.fn(),
|
||||
});
|
||||
|
||||
loadLanguage('en', englishBaseText);
|
||||
loadLanguage('en', englishBaseText as LocaleMessages);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { loadLanguage } from '@n8n/i18n';
|
||||
import type { LocaleMessages } from '@n8n/i18n/types';
|
||||
|
||||
export async function loadDefaultEn() {
|
||||
const mod = (await import('@n8n/i18n/locales/en.json')) as { default: LocaleMessages };
|
||||
loadLanguage('en', mod.default);
|
||||
}
|
||||
@@ -11,11 +11,13 @@ import '@n8n/design-system/css/index.scss';
|
||||
// import '@n8n/design-system/css/tailwind/index.css';
|
||||
|
||||
import './n8n-theme.scss';
|
||||
// Ensure i18n HMR owner is evaluated as early as possible in dev
|
||||
import '@/dev/i18nHmr';
|
||||
|
||||
import App from '@/App.vue';
|
||||
import router from './router';
|
||||
|
||||
import { i18nInstance, setLanguage } from '@n8n/i18n';
|
||||
import { i18nInstance } from '@n8n/i18n';
|
||||
|
||||
import { TelemetryPlugin } from './plugins/telemetry';
|
||||
import { GlobalComponentsPlugin } from './plugins/components';
|
||||
@@ -35,17 +37,6 @@ const app = createApp(App);
|
||||
|
||||
app.use(SentryPlugin);
|
||||
|
||||
// Initialize i18n
|
||||
if (import.meta.env.DEV) {
|
||||
// Import HMR owner early so messages are seeded before app mount
|
||||
await import('@/dev/i18nHmr');
|
||||
setLanguage('en');
|
||||
} else {
|
||||
// Production: load English messages explicitly via isolated module
|
||||
const { loadDefaultEn } = await import('@/i18n/loadDefaultEn');
|
||||
await loadDefaultEn();
|
||||
}
|
||||
|
||||
// Register module routes
|
||||
// We do this here so landing straight on a module page works
|
||||
registerModuleRoutes(router);
|
||||
|
||||
Reference in New Issue
Block a user