feat(i18n): i18n auto-importer

This commit is contained in:
JellyBrick
2023-12-02 02:13:49 +09:00
parent c8554a12f6
commit fd3438a20d
8 changed files with 75 additions and 16 deletions

View File

@ -9,6 +9,7 @@ import { pluginVirtualModuleGenerator } from './vite-plugins/plugin-importer';
import pluginLoader from './vite-plugins/plugin-loader'; import pluginLoader from './vite-plugins/plugin-loader';
import type { UserConfig } from 'vite'; import type { UserConfig } from 'vite';
import { i18nImporter } from './vite-plugins/i18n-importer';
const resolveAlias = { const resolveAlias = {
'@': resolve(__dirname, './src'), '@': resolve(__dirname, './src'),
@ -21,6 +22,7 @@ export default defineConfig({
plugins: [ plugins: [
pluginLoader('backend'), pluginLoader('backend'),
viteResolve({ viteResolve({
'virtual:i18n': i18nImporter(),
'virtual:plugins': pluginVirtualModuleGenerator('main'), 'virtual:plugins': pluginVirtualModuleGenerator('main'),
}), }),
], ],
@ -65,6 +67,7 @@ export default defineConfig({
plugins: [ plugins: [
pluginLoader('preload'), pluginLoader('preload'),
viteResolve({ viteResolve({
'virtual:i18n': i18nImporter(),
'virtual:plugins': pluginVirtualModuleGenerator('preload'), 'virtual:plugins': pluginVirtualModuleGenerator('preload'),
}), }),
], ],
@ -108,6 +111,7 @@ export default defineConfig({
plugins: [ plugins: [
pluginLoader('renderer'), pluginLoader('renderer'),
viteResolve({ viteResolve({
'virtual:i18n': i18nImporter(),
'virtual:plugins': pluginVirtualModuleGenerator('renderer'), 'virtual:plugins': pluginVirtualModuleGenerator('renderer'),
}), }),
], ],

View File

@ -1,6 +1,6 @@
import i18next, { init, t as i18t, changeLanguage } from 'i18next'; import i18next, { init, t as i18t, changeLanguage } from 'i18next';
import { languageResources } from '@/i18n/resources'; import { languageResources } from 'virtual:i18n';
export const loadI18n = async () => export const loadI18n = async () =>
await init({ await init({

View File

@ -0,0 +1,11 @@
export interface LanguageResources {
[lang: string]: {
translation: Record<string, unknown> & {
language: {
name: string;
'local-name': string;
code: string;
};
};
};
}

View File

@ -1,11 +0,0 @@
import enJson from './en.json';
import koJson from './ko.json';
export const languageResources = {
en: {
translation: enJson
},
ko: {
translation: koJson
}
};

View File

@ -26,6 +26,8 @@ import { deepEqual } from 'fast-equals';
import { allPlugins, mainPlugins } from 'virtual:plugins'; import { allPlugins, mainPlugins } from 'virtual:plugins';
import { languageResources } from 'virtual:i18n';
import config from '@/config'; import config from '@/config';
import { refreshMenu, setApplicationMenu } from '@/menu'; import { refreshMenu, setApplicationMenu } from '@/menu';
@ -52,8 +54,6 @@ import {
import { LoggerPrefix } from '@/utils'; import { LoggerPrefix } from '@/utils';
import { loadI18n, setLanguage, t } from '@/i18n'; import { loadI18n, setLanguage, t } from '@/i18n';
import { languageResources } from '@/i18n/resources';
import type { PluginConfig } from '@/types/plugins'; import type { PluginConfig } from '@/types/plugins';
// Catch errors and log them // Catch errors and log them

View File

@ -11,6 +11,8 @@ import prompt from 'custom-electron-prompt';
import { allPlugins } from 'virtual:plugins'; import { allPlugins } from 'virtual:plugins';
import { languageResources } from 'virtual:i18n';
import config from './config'; import config from './config';
import { restart } from './providers/app-controls'; import { restart } from './providers/app-controls';
@ -19,7 +21,7 @@ import promptOptions from './providers/prompt-options';
import { getAllMenuTemplate, loadAllMenuPlugins } from './loader/menu'; import { getAllMenuTemplate, loadAllMenuPlugins } from './loader/menu';
import { setLanguage, t } from '@/i18n'; import { setLanguage, t } from '@/i18n';
import { languageResources } from '@/i18n/resources';
export type MenuTemplate = Electron.MenuItemConstructorOptions[]; export type MenuTemplate = Electron.MenuItemConstructorOptions[];
@ -104,7 +106,7 @@ export const mainMenuTemplate = async (
return pluginEnabledMenu(id, pluginLabel, true, innerRefreshMenu); return pluginEnabledMenu(id, pluginLabel, true, innerRefreshMenu);
}); });
const availableLanguages = Object.keys(languageResources) as unknown as (keyof typeof languageResources)[]; const availableLanguages = Object.keys(languageResources);
return [ return [
{ {

View File

@ -12,3 +12,9 @@ declare module 'virtual:plugins' {
Omit<Plugin, 'backend' | 'preload' | 'renderer'> Omit<Plugin, 'backend' | 'preload' | 'renderer'>
>; >;
} }
declare module 'virtual:i18n' {
import type { LanguageResources } from '@/i18n/resources/@types';
export const languageResources: LanguageResources;
}

View File

@ -0,0 +1,47 @@
import { basename, relative, resolve, extname } from 'node:path';
import { globSync } from 'glob';
import { Project } from 'ts-morph';
const snakeToCamel = (text: string) =>
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
export const i18nImporter = () => {
const project = new Project({
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
skipAddingFilesFromTsConfig: true,
skipLoadingLibFiles: true,
skipFileDependencyResolution: true,
});
const srcPath = resolve(__dirname, '..', 'src');
const plugins = globSync([
'src/i18n/resources/*.json',
]).map((path) => {
const nameWithExt = basename(path);
const name = nameWithExt.replace(extname(nameWithExt), '');
return { name, path };
});
const src = project.createSourceFile('vm:i18n', (writer) => {
// prettier-ignore
for (const { name, path } of plugins) {
const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/');
writer.writeLine(`import ${snakeToCamel(name)}Json from "./${relativePath}";`);
}
writer.blankLine();
writer.writeLine('export const languageResources = {');
for (const { name } of plugins) {
writer.writeLine(` "${name}": {`);
writer.writeLine(` translation: ${snakeToCamel(name)}Json,`);
writer.writeLine(' },');
}
writer.writeLine('};');
writer.blankLine();
});
return src.getText();
};