diff options
| author | anatawa12 <anatawa12@icloud.com> | 2025-08-08 11:26:18 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-08 11:26:18 +0900 |
| commit | 8598f3912ecc16f9b7c3f502e09d9ea96f7e507d (patch) | |
| tree | 73b24c7ab473189a679ae6aa476b6822f812cbed /packages/backend/src/server/web | |
| parent | Update CONTRIBUTING.md (diff) | |
| download | misskey-8598f3912ecc16f9b7c3f502e09d9ea96f7e507d.tar.gz misskey-8598f3912ecc16f9b7c3f502e09d9ea96f7e507d.tar.bz2 misskey-8598f3912ecc16f9b7c3f502e09d9ea96f7e507d.zip | |
per-locale bundle & inline locale (#16369)
* feat: split entry file by locale name
* chore: とりあえず transform hook で雑に分割
* chore: とりあえず transform 結果をいい感じに
* chore: concurrent buildで高速化
* chore: vite ではローケルのないものをビルドして後処理でどうにかするように
* chore: 後処理のためにi18n.jを単体になるように切り出す
* chore: use typescript
* chore: remove unref(i18n) in vite build process
* chore: inline variable
* fix: build error
* fix: i18n.ts.something.replaceAll() become error
* chore: ignore export specifier from error
* chore: support i18n.tsx as object
* chore: process literal for all files
* chore: split config and locale
* chore: inline locale name
* chore: remove updating locale in boot common
* chore: use top-level await to load locales
* chore: inline locale
* chore: remove loading locale from boot.js
* chore: remove loading locale from boot.js
* コメント追加
* fix test; fetchに失敗する
* import削除ログをdebugレベルに
* fix: watch pug
* chore: use hash for entry files
* chore: remove es-module-lexer from dependencies
* chore: move to frontend-builder
* chore: use inline locale in embed
* chore: refetch json on hot reload
* feat: store localization related to boot.js in backend in bootloaderLocales localstorage
* 応急処置を戻す
* fix spex
* fix `Using i18n identifier "e" directly. Skipping inlining.` warning
* refactor: use scriptsDir parameter
* chore: remove i18n from depmap
* chore: make build crash if errors
* error -> warn few conditions
* use inline object
* update localstorage keys
* remove accessing locale localstorage
* fix: failed to process i18n.tsx.aaa({x:i18n.bbb})
Diffstat (limited to 'packages/backend/src/server/web')
| -rw-r--r-- | packages/backend/src/server/web/boot.embed.js | 85 | ||||
| -rw-r--r-- | packages/backend/src/server/web/boot.js | 91 | ||||
| -rw-r--r-- | packages/backend/src/server/web/views/base-embed.pug | 3 | ||||
| -rw-r--r-- | packages/backend/src/server/web/views/base.pug | 3 |
4 files changed, 75 insertions, 107 deletions
diff --git a/packages/backend/src/server/web/boot.embed.js b/packages/backend/src/server/web/boot.embed.js index 9de1275380..022ff064ad 100644 --- a/packages/backend/src/server/web/boot.embed.js +++ b/packages/backend/src/server/web/boot.embed.js @@ -32,61 +32,30 @@ } //#region Detect language & fetch translations - if (!localStorage.hasOwnProperty('locale')) { - const supportedLangs = LANGS; - let lang = localStorage.getItem('lang'); - if (lang == null || !supportedLangs.includes(lang)) { - if (supportedLangs.includes(navigator.language)) { - lang = navigator.language; - } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); - - // Fallback - if (lang == null) lang = 'en-US'; - } - } - - const metaRes = await window.fetch('/api/meta', { - method: 'POST', - body: JSON.stringify({}), - credentials: 'omit', - cache: 'no-cache', - headers: { - 'Content-Type': 'application/json', - }, - }); - if (metaRes.status !== 200) { - renderError('META_FETCH'); - return; - } - const meta = await metaRes.json(); - const v = meta.version; - if (v == null) { - renderError('META_FETCH_V'); - return; - } + const supportedLangs = LANGS; + /** @type { string } */ + let lang = localStorage.getItem('lang'); + if (lang == null || !supportedLangs.includes(lang)) { + if (supportedLangs.includes(navigator.language)) { + lang = navigator.language; + } else { + lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); - // for https://github.com/misskey-dev/misskey/issues/10202 - if (lang == null || lang.toString == null || lang.toString() === 'null') { - console.error('invalid lang value detected!!!', typeof lang, lang); - lang = 'en-US'; + // Fallback + if (lang == null) lang = 'en-US'; } + } - const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`); - if (localRes.status === 200) { - localStorage.setItem('lang', lang); - localStorage.setItem('locale', await localRes.text()); - localStorage.setItem('localeVersion', v); - } else { - renderError('LOCALE_FETCH'); - return; - } + // for https://github.com/misskey-dev/misskey/issues/10202 + if (lang == null || lang.toString == null || lang.toString() === 'null') { + console.error('invalid lang value detected!!!', typeof lang, lang); + lang = 'en-US'; } //#endregion //#region Script async function importAppScript() { - await import(`/embed_vite/${CLIENT_ENTRY}`) + await import(CLIENT_ENTRY ? `/embed_vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/embed_vite/src/_boot_.ts') .catch(async e => { console.error(e); renderError('APP_IMPORT'); @@ -115,10 +84,26 @@ await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve)); } - const locale = JSON.parse(localStorage.getItem('locale') || '{}'); + let messages = null; + const bootloaderLocales = localStorage.getItem('bootloaderLocales'); + if (bootloaderLocales) { + messages = JSON.parse(bootloaderLocales); + } + if (!messages) { + // older version of misskey does not store bootloaderLocales, stores locale as a whole + const legacyLocale = localStorage.getItem('locale'); + if (legacyLocale) { + const parsed = JSON.parse(legacyLocale); + messages = { + ...(parsed._bootErrors ?? {}), + reload: parsed.reload, + }; + } + } + if (!messages) messages = {}; - const title = locale?._bootErrors?.title || 'Failed to initialize Misskey'; - const reload = locale?.reload || 'Reload'; + const title = messages?.title || 'Failed to initialize Misskey'; + const reload = messages?.reload || 'Reload'; document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg> <div class="message">${title}</div> diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 1a30e9ed2b..0c0b46f82b 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -22,62 +22,31 @@ return; } - //#region Detect language & fetch translations - if (!localStorage.hasOwnProperty('locale')) { - const supportedLangs = LANGS; - let lang = localStorage.getItem('lang'); - if (lang == null || !supportedLangs.includes(lang)) { - if (supportedLangs.includes(navigator.language)) { - lang = navigator.language; - } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); - - // Fallback - if (lang == null) lang = 'en-US'; - } - } - - const metaRes = await window.fetch('/api/meta', { - method: 'POST', - body: JSON.stringify({}), - credentials: 'omit', - cache: 'no-cache', - headers: { - 'Content-Type': 'application/json', - }, - }); - if (metaRes.status !== 200) { - renderError('META_FETCH'); - return; - } - const meta = await metaRes.json(); - const v = meta.version; - if (v == null) { - renderError('META_FETCH_V'); - return; - } + //#region Detect language + const supportedLangs = LANGS; + /** @type { string } */ + let lang = localStorage.getItem('lang'); + if (lang == null || !supportedLangs.includes(lang)) { + if (supportedLangs.includes(navigator.language)) { + lang = navigator.language; + } else { + lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); - // for https://github.com/misskey-dev/misskey/issues/10202 - if (lang == null || lang.toString == null || lang.toString() === 'null') { - console.error('invalid lang value detected!!!', typeof lang, lang); - lang = 'en-US'; + // Fallback + if (lang == null) lang = 'en-US'; } + } - const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`); - if (localRes.status === 200) { - localStorage.setItem('lang', lang); - localStorage.setItem('locale', await localRes.text()); - localStorage.setItem('localeVersion', v); - } else { - renderError('LOCALE_FETCH'); - return; - } + // for https://github.com/misskey-dev/misskey/issues/10202 + if (lang == null || lang.toString == null || lang.toString() === 'null') { + console.error('invalid lang value detected!!!', typeof lang, lang); + lang = 'en-US'; } //#endregion //#region Script async function importAppScript() { - await import(`/vite/${CLIENT_ENTRY}`) + await import(CLIENT_ENTRY ? `/vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/vite/src/_boot_.ts') .catch(async e => { console.error(e); renderError('APP_IMPORT', e); @@ -162,9 +131,25 @@ await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve)); } - const locale = JSON.parse(localStorage.getItem('locale') || '{}'); + let messages = null; + const bootloaderLocales = localStorage.getItem('bootloaderLocales'); + if (bootloaderLocales) { + messages = JSON.parse(bootloaderLocales); + } + if (!messages) { + // older version of misskey does not store bootloaderLocales, stores locale as a whole + const legacyLocale = localStorage.getItem('locale'); + if (legacyLocale) { + const parsed = JSON.parse(legacyLocale); + messages = { + ...(parsed._bootErrors ?? {}), + reload: parsed.reload, + }; + } + } + if (!messages) messages = {}; - const messages = Object.assign({ + messages = Object.assign({ title: 'Failed to initialize Misskey', solution: 'The following actions may solve the problem.', solution1: 'Update your os and browser', @@ -176,8 +161,8 @@ otherOption2: 'Start the simple client', otherOption3: 'Start the repair tool', otherOption4: 'Start Misskey in safe mode', - }, locale?._bootErrors || {}); - const reload = locale?.reload || 'Reload'; + reload: 'Reload', + }, messages); const safeModeUrl = new URL(window.location.href); safeModeUrl.searchParams.set('safemode', 'true'); @@ -193,7 +178,7 @@ </svg> <h1>${messages.title}</h1> <button class="button-big" onclick="location.reload(true);"> - <span class="button-label-big">${reload}</span> + <span class="button-label-big">${messages?.reload}</span> </button> <p><b>${messages.solution}</b></p> <p>${messages.solution1}</p> diff --git a/packages/backend/src/server/web/views/base-embed.pug b/packages/backend/src/server/web/views/base-embed.pug index baa0909676..29de86b8b6 100644 --- a/packages/backend/src/server/web/views/base-embed.pug +++ b/packages/backend/src/server/web/views/base-embed.pug @@ -19,7 +19,6 @@ html(class='embed') meta(name='format-detection' content='telephone=no,date=no,address=no,email=no,url=no') link(rel='icon' href= icon || '/favicon.ico') link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png') - link(rel='modulepreload' href=`/embed_vite/${entry.file}`) if !config.frontendEmbedManifestExists script(type="module" src="/embed_vite/@vite/client") @@ -40,7 +39,7 @@ html(class='embed') script. var VERSION = "#{version}"; - var CLIENT_ENTRY = "#{entry.file}"; + var CLIENT_ENTRY = !{JSON.stringify(entry.file)}; script(type='application/json' id='misskey_meta' data-generated-at=now) != metaJson diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 3883b5e5ab..a76c75fe5c 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -37,7 +37,6 @@ html link(rel='prefetch' href=serverErrorImageUrl) link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=notFoundImageUrl) - link(rel='modulepreload' href=`/vite/${entry.file}`) if !config.frontendManifestExists script(type="module" src="/vite/@vite/client") @@ -69,7 +68,7 @@ html script. var VERSION = "#{version}"; - var CLIENT_ENTRY = "#{entry.file}"; + var CLIENT_ENTRY = !{JSON.stringify(entry.file)}; script(type='application/json' id='misskey_meta' data-generated-at=now) != metaJson |