feat: i18n support with first translations

This commit is contained in:
2026-02-21 10:45:41 +01:00
parent a5281a7750
commit b8005bec30
48 changed files with 2792 additions and 462 deletions

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest';
import {
translateUi,
resolveSupportedUiLanguage,
resolveUiLanguageFromSystemLocale,
} from '../../src/renderer/i18n';
describe('renderer i18n', () => {
it('resolves supported ui language from OS locale', () => {
expect(resolveUiLanguageFromSystemLocale('de-DE')).toBe('de');
expect(resolveUiLanguageFromSystemLocale('fr-CH')).toBe('fr');
expect(resolveUiLanguageFromSystemLocale('pt-BR')).toBe('en');
});
it('normalizes explicit ui language values', () => {
expect(resolveSupportedUiLanguage('it')).toBe('it');
expect(resolveSupportedUiLanguage('es-MX')).toBe('es');
expect(resolveSupportedUiLanguage('')).toBe('en');
});
it('returns translated text with english fallback', () => {
expect(translateUi('de', 'common.save')).toBe('Speichern');
expect(translateUi('fr', 'common.cancel')).toBe('Annuler');
expect(translateUi('de', 'settings.language.english')).toBe('Englisch');
expect(translateUi('it', 'missing.key')).toBe('missing.key');
});
});

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest';
import en from '../../src/renderer/i18n/locales/en.json';
import de from '../../src/renderer/i18n/locales/de.json';
import fr from '../../src/renderer/i18n/locales/fr.json';
import itLocale from '../../src/renderer/i18n/locales/it.json';
import es from '../../src/renderer/i18n/locales/es.json';
type LocaleMap = Record<string, string>;
const locales: Record<string, LocaleMap> = {
de: de as LocaleMap,
fr: fr as LocaleMap,
it: itLocale as LocaleMap,
es: es as LocaleMap,
};
describe('renderer locale completeness', () => {
const englishKeys = Object.keys(en as LocaleMap).sort();
it('all renderer locales contain exactly the english key set', () => {
for (const [locale, messages] of Object.entries(locales)) {
const localeKeys = Object.keys(messages).sort();
expect(localeKeys, `Locale ${locale} is missing or has extra keys`).toEqual(englishKeys);
}
});
});

View File

@@ -40,12 +40,11 @@ describe('Help menu documentation entry', () => {
expect(viewGroup?.items.some((item) => item.action === 'toggleFullScreen')).toBe(true);
});
it('renames generateSitemap menu item to Render Site and assigns Command/Ctrl+R', () => {
it('assigns Command/Ctrl+R shortcut for generateSitemap menu item', () => {
const blogGroup = APP_MENU_GROUPS.find((group) => group.label === 'Blog');
const generateSiteItem = blogGroup?.items.find((item) => item.action === 'generateSitemap');
expect(generateSiteItem).toBeDefined();
expect(generateSiteItem?.label).toBe('Render Site');
expect(generateSiteItem?.accelerator).toBe('CmdOrCtrl+R');
});
});