diff --git a/packages/frontend/lib/vite-plugin-watch-locales.ts b/packages/frontend/lib/vite-plugin-watch-locales.ts new file mode 100644 index 0000000000..8e209d27bd --- /dev/null +++ b/packages/frontend/lib/vite-plugin-watch-locales.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import path from 'node:path' +import locales from '../../../locales/index.js'; + +const localesDir = path.resolve(__dirname, '../../../locales') + +/** + * 外部ファイルを監視し、必要に応じてwebSocketでメッセージを送るViteプラグイン + * @returns {import('vite').Plugin} + */ +export default function pluginWatchLocales() { + return { + name: 'watch-locales', + + configureServer(server) { + const localeYmlPaths = Object.keys(locales).map(locale => path.join(localesDir, `${locale}.yml`)); + + // watcherにパスを追加 + server.watcher.add(localeYmlPaths); + + server.watcher.on('change', (filePath) => { + if (localeYmlPaths.includes(filePath)) { + server.ws.send({ + type: 'custom', + event: 'locale-update', + data: filePath.match(/([^\/]+)\.yml$/)?.[1] || null, + }) + } + }); + }, + }; +} diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index c8098b6cf8..992bde9bd1 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -81,8 +81,10 @@ export async function common(createVue: () => Promise>) { //#region Detect language & fetch translations const localeVersion = miLocalStorage.getItem('localeVersion'); const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null); - if (localeOutdated) { - const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); + + async function fetchAndUpdateLocale({ useCache } = { useCache: true }) { + const fetchOptions: RequestInit | undefined = useCache ? undefined : { cache: 'no-store' }; + const res = await window.fetch(`/assets/locales/${lang}.${version}.json`, fetchOptions); if (res.status === 200) { const newLocale = await res.text(); const parsedNewLocale = JSON.parse(newLocale); @@ -92,6 +94,23 @@ export async function common(createVue: () => Promise>) { updateI18n(parsedNewLocale); } } + + if (localeOutdated) { + fetchAndUpdateLocale(); + } + + if (import.meta.hot) { + import.meta.hot.on('locale-update', async (updatedLang: string) => { + console.info(`Locale updated: ${updatedLang}`); + if (updatedLang === lang) { + await new Promise(resolve => { + window.setTimeout(resolve, 500); + }); + await fetchAndUpdateLocale({ useCache: false }); + window.location.reload(); + } + }); + } //#endregion // タッチデバイスでCSSの:hoverを機能させる diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index b0ccbfb65c..77b5b48a18 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -13,6 +13,7 @@ import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-modul import pluginJson5 from './vite.json5.js'; import pluginCreateSearchIndex from './lib/vite-plugin-create-search-index.js'; import type { Options as SearchIndexOptions } from './lib/vite-plugin-create-search-index.js'; +import pluginWatchLocales from './lib/vite-plugin-watch-locales.js'; const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null; const host = url ? (new URL(url)).hostname : undefined; @@ -102,6 +103,7 @@ export function getConfig(): UserConfig { }, plugins: [ + pluginWatchLocales(), ...searchIndexes.map(options => pluginCreateSearchIndex(options)), pluginVue(), pluginUnwindCssModuleClassName(),