-
Notifications
You must be signed in to change notification settings - Fork 447
fix: preserve custom nodes i18n data when locales are lazily loaded #7214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+227
−4
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| import { beforeEach, describe, expect, it, vi } from 'vitest' | ||
| const { i18n, loadLocale, mergeCustomNodesI18n } = await import('./i18n') | ||
|
|
||
| // Mock the JSON imports before importing i18n module | ||
| vi.mock('./locales/en/main.json', () => ({ default: { welcome: 'Welcome' } })) | ||
| vi.mock('./locales/en/nodeDefs.json', () => ({ | ||
| default: { testNode: 'Test Node' } | ||
| })) | ||
| vi.mock('./locales/en/commands.json', () => ({ | ||
| default: { save: 'Save' } | ||
| })) | ||
| vi.mock('./locales/en/settings.json', () => ({ | ||
| default: { theme: 'Theme' } | ||
| })) | ||
|
|
||
| // Mock lazy-loaded locales | ||
| vi.mock('./locales/zh/main.json', () => ({ default: { welcome: '欢迎' } })) | ||
| vi.mock('./locales/zh/nodeDefs.json', () => ({ | ||
| default: { testNode: '测试节点' } | ||
| })) | ||
| vi.mock('./locales/zh/commands.json', () => ({ default: { save: '保存' } })) | ||
| vi.mock('./locales/zh/settings.json', () => ({ default: { theme: '主题' } })) | ||
|
|
||
| describe('i18n', () => { | ||
| beforeEach(async () => { | ||
| vi.resetModules() | ||
| }) | ||
|
|
||
| describe('mergeCustomNodesI18n', () => { | ||
| it('should immediately merge data for already loaded locales (en)', async () => { | ||
| // English is pre-loaded, so merge should work immediately | ||
| mergeCustomNodesI18n({ | ||
| en: { | ||
| customNode: { | ||
| title: 'Custom Node Title' | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| // Verify the custom node data was merged | ||
| const messages = i18n.global.getLocaleMessage('en') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| expect(messages.customNode).toEqual({ title: 'Custom Node Title' }) | ||
| }) | ||
|
|
||
| it('should store data for not-yet-loaded locales', async () => { | ||
| const { i18n, mergeCustomNodesI18n } = await import('./i18n') | ||
|
|
||
| // Chinese is not pre-loaded, data should be stored but not merged yet | ||
| mergeCustomNodesI18n({ | ||
| zh: { | ||
| customNode: { | ||
| title: '自定义节点标题' | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| // zh locale should not exist yet (not loaded) | ||
| const zhMessages = i18n.global.getLocaleMessage('zh') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| // Either empty or doesn't have our custom data merged directly | ||
| // (since zh wasn't loaded yet, mergeLocaleMessage on non-existent locale | ||
| // may create an empty locale or do nothing useful) | ||
| expect(zhMessages.customNode).toBeUndefined() | ||
| }) | ||
|
|
||
| it('should merge stored data when locale is lazily loaded', async () => { | ||
| // First, store custom nodes i18n data (before locale is loaded) | ||
| mergeCustomNodesI18n({ | ||
| zh: { | ||
| customNode: { | ||
| title: '自定义节点标题' | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| await loadLocale('zh') | ||
|
|
||
| // Verify both the base locale data and custom node data are present | ||
| const zhMessages = i18n.global.getLocaleMessage('zh') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| expect(zhMessages.welcome).toBe('欢迎') | ||
| expect(zhMessages.customNode).toEqual({ title: '自定义节点标题' }) | ||
| }) | ||
|
|
||
| it('should preserve custom node data when locale is loaded after merge', async () => { | ||
| // Simulate the real scenario: | ||
| // 1. Custom nodes i18n is loaded first | ||
| mergeCustomNodesI18n({ | ||
| zh: { | ||
| customNode: { | ||
| title: '自定义节点标题' | ||
| }, | ||
| settingsCategories: { | ||
| Hotkeys: '快捷键' | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| // 2. Then locale is lazily loaded (this would previously overwrite custom data) | ||
| await loadLocale('zh') | ||
|
|
||
| // 3. Verify custom node data is still present | ||
| const zhMessages = i18n.global.getLocaleMessage('zh') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| expect(zhMessages.customNode).toEqual({ title: '自定义节点标题' }) | ||
| expect(zhMessages.settingsCategories).toEqual({ Hotkeys: '快捷键' }) | ||
|
|
||
| // 4. Also verify base locale data is present | ||
| expect(zhMessages.welcome).toBe('欢迎') | ||
| expect(zhMessages.nodeDefs).toEqual({ testNode: '测试节点' }) | ||
| }) | ||
|
|
||
| it('should handle multiple locales in custom nodes i18n data', async () => { | ||
| // Merge data for multiple locales | ||
| mergeCustomNodesI18n({ | ||
| en: { | ||
| customPlugin: { name: 'Easy Use' } | ||
| }, | ||
| zh: { | ||
| customPlugin: { name: '简单使用' } | ||
| } | ||
| }) | ||
|
|
||
| // English should be merged immediately (pre-loaded) | ||
| const enMessages = i18n.global.getLocaleMessage('en') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| expect(enMessages.customPlugin).toEqual({ name: 'Easy Use' }) | ||
|
|
||
| await loadLocale('zh') | ||
| const zhMessages = i18n.global.getLocaleMessage('zh') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| expect(zhMessages.customPlugin).toEqual({ name: '简单使用' }) | ||
| }) | ||
|
|
||
| it('should handle calling mergeCustomNodesI18n multiple times', async () => { | ||
| // Use fresh module instance to ensure clean state | ||
| vi.resetModules() | ||
| const { i18n, loadLocale, mergeCustomNodesI18n } = await import('./i18n') | ||
|
|
||
| mergeCustomNodesI18n({ | ||
| zh: { plugin1: { name: '插件1' } } | ||
| }) | ||
|
|
||
| mergeCustomNodesI18n({ | ||
| zh: { plugin2: { name: '插件2' } } | ||
| }) | ||
|
|
||
| await loadLocale('zh') | ||
|
|
||
| const zhMessages = i18n.global.getLocaleMessage('zh') as Record< | ||
| string, | ||
| unknown | ||
| > | ||
| // Only the second call's data should be present | ||
| expect(zhMessages.plugin2).toEqual({ name: '插件2' }) | ||
| // First call's data is overwritten | ||
| expect(zhMessages.plugin1).toBeUndefined() | ||
| }) | ||
| }) | ||
|
|
||
| describe('loadLocale', () => { | ||
| it('should not reload already loaded locale', async () => { | ||
| await loadLocale('zh') | ||
| await loadLocale('zh') | ||
|
|
||
| // Should complete without error (second call returns early) | ||
| }) | ||
|
|
||
| it('should warn for unsupported locale', async () => { | ||
| const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) | ||
|
|
||
| await loadLocale('unsupported-locale') | ||
|
|
||
| expect(consoleSpy).toHaveBeenCalledWith( | ||
| 'Locale "unsupported-locale" is not supported' | ||
| ) | ||
| consoleSpy.mockRestore() | ||
| }) | ||
|
|
||
| it('should handle concurrent load requests for same locale', async () => { | ||
| // Start multiple loads concurrently | ||
| const promises = [loadLocale('zh'), loadLocale('zh'), loadLocale('zh')] | ||
|
|
||
| await Promise.all(promises) | ||
| }) | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.