mirror of
https://activitypub.software/TransFem-org/Sharkey
synced 2024-11-24 15:05:13 +00:00
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/692 Closes #732 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
commit
68b90df00b
8 changed files with 41 additions and 31 deletions
1
locales/version.d.ts
vendored
Normal file
1
locales/version.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const localesVersion: string;
|
14
locales/version.js
Normal file
14
locales/version.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
import locales from './index.js';
|
||||||
|
|
||||||
|
// MD5 is acceptable because we don't need cryptographic security.
|
||||||
|
const hash = createHash('md5');
|
||||||
|
|
||||||
|
// Derive the version hash from locale content exclusively.
|
||||||
|
// This avoids the problem of "stuck" translations after modifying locale files.
|
||||||
|
const localesText = JSON.stringify(locales);
|
||||||
|
hash.update(localesText, 'utf8');
|
||||||
|
|
||||||
|
// We can't use regular base64 since this becomes part of a filename.
|
||||||
|
// Base64URL avoids special characters that would cause an issue.
|
||||||
|
export const localesVersion = hash.digest().toString('base64url');
|
|
@ -33,8 +33,17 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force update when locales change
|
||||||
|
const langsVersion = LANGS_VERSION;
|
||||||
|
const localeVersion = localStorage.getItem('localeVersion');
|
||||||
|
if (localeVersion !== langsVersion) {
|
||||||
|
console.info(`Updating locales from version ${localeVersion ?? 'N/A'} to ${langsVersion}`);
|
||||||
|
localStorage.removeItem('localeVersion');
|
||||||
|
localStorage.removeItem('locale');
|
||||||
|
}
|
||||||
|
|
||||||
//#region Detect language & fetch translations
|
//#region Detect language & fetch translations
|
||||||
if (!localStorage.hasOwnProperty('locale')) {
|
if (!localStorage.getItem('locale')) {
|
||||||
const supportedLangs = LANGS;
|
const supportedLangs = LANGS;
|
||||||
let lang = localStorage.getItem('lang');
|
let lang = localStorage.getItem('lang');
|
||||||
if (lang == null || !supportedLangs.includes(lang)) {
|
if (lang == null || !supportedLangs.includes(lang)) {
|
||||||
|
@ -48,37 +57,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for https://github.com/misskey-dev/misskey/issues/10202
|
// for https://github.com/misskey-dev/misskey/issues/10202
|
||||||
if (lang == null || lang.toString == null || lang.toString() === 'null') {
|
if (lang == null || lang.toString == null || lang.toString() === 'null') {
|
||||||
console.error('invalid lang value detected!!!', typeof lang, lang);
|
console.error('invalid lang value detected!!!', typeof lang, lang);
|
||||||
lang = 'en-US';
|
lang = 'en-US';
|
||||||
}
|
}
|
||||||
|
|
||||||
const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
const localRes = await window.fetch(`/assets/locales/${lang}.${langsVersion}.json`);
|
||||||
if (localRes.status === 200) {
|
if (localRes.status === 200) {
|
||||||
localStorage.setItem('lang', lang);
|
localStorage.setItem('lang', lang);
|
||||||
localStorage.setItem('locale', await localRes.text());
|
localStorage.setItem('locale', await localRes.text());
|
||||||
localStorage.setItem('localeVersion', v);
|
localStorage.setItem('localeVersion', langsVersion);
|
||||||
} else {
|
} else {
|
||||||
renderError('LOCALE_FETCH');
|
renderError('LOCALE_FETCH');
|
||||||
return;
|
return;
|
||||||
|
|
1
packages/frontend/@types/global.d.ts
vendored
1
packages/frontend/@types/global.d.ts
vendored
|
@ -6,6 +6,7 @@
|
||||||
type FIXME = any;
|
type FIXME = any;
|
||||||
|
|
||||||
declare const _LANGS_: string[][];
|
declare const _LANGS_: string[][];
|
||||||
|
declare const _LANGS_VERSION_: string;
|
||||||
declare const _VERSION_: string;
|
declare const _VERSION_: string;
|
||||||
declare const _ENV_: string;
|
declare const _ENV_: string;
|
||||||
declare const _DEV_: boolean;
|
declare const _DEV_: boolean;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { compareVersions } from 'compare-versions';
|
||||||
import widgets from '@/widgets/index.js';
|
import widgets from '@/widgets/index.js';
|
||||||
import directives from '@/directives/index.js';
|
import directives from '@/directives/index.js';
|
||||||
import components from '@/components/index.js';
|
import components from '@/components/index.js';
|
||||||
import { version, lang, updateLocale, locale } from '@/config.js';
|
import { version, lang, langsVersion, updateLocale, locale } from '@/config.js';
|
||||||
import { applyTheme } from '@/scripts/theme.js';
|
import { applyTheme } from '@/scripts/theme.js';
|
||||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
||||||
import { updateI18n } from '@/i18n.js';
|
import { updateI18n } from '@/i18n.js';
|
||||||
|
@ -80,14 +80,15 @@ export async function common(createVue: () => App<Element>) {
|
||||||
|
|
||||||
//#region Detect language & fetch translations
|
//#region Detect language & fetch translations
|
||||||
const localeVersion = miLocalStorage.getItem('localeVersion');
|
const localeVersion = miLocalStorage.getItem('localeVersion');
|
||||||
const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
|
const localeOutdated = (localeVersion == null || localeVersion !== langsVersion || locale == null);
|
||||||
if (localeOutdated) {
|
if (localeOutdated) {
|
||||||
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
|
console.info(`Updating locales from version ${localeVersion ?? 'N/A'} to ${langsVersion}`);
|
||||||
|
const res = await window.fetch(`/assets/locales/${lang}.${langsVersion}.json`);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const newLocale = await res.text();
|
const newLocale = await res.text();
|
||||||
const parsedNewLocale = JSON.parse(newLocale);
|
const parsedNewLocale = JSON.parse(newLocale);
|
||||||
miLocalStorage.setItem('locale', newLocale);
|
miLocalStorage.setItem('locale', newLocale);
|
||||||
miLocalStorage.setItem('localeVersion', version);
|
miLocalStorage.setItem('localeVersion', langsVersion);
|
||||||
updateLocale(parsedNewLocale);
|
updateLocale(parsedNewLocale);
|
||||||
updateI18n(parsedNewLocale);
|
updateI18n(parsedNewLocale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const apiUrl = location.origin + '/api';
|
||||||
export const wsOrigin = location.origin;
|
export const wsOrigin = location.origin;
|
||||||
export const lang = miLocalStorage.getItem('lang') ?? 'en-US';
|
export const lang = miLocalStorage.getItem('lang') ?? 'en-US';
|
||||||
export const langs = _LANGS_;
|
export const langs = _LANGS_;
|
||||||
|
export const langsVersion = _LANGS_VERSION_;
|
||||||
const preParseLocale = miLocalStorage.getItem('locale');
|
const preParseLocale = miLocalStorage.getItem('locale');
|
||||||
export let locale = preParseLocale ? JSON.parse(preParseLocale) : null;
|
export let locale = preParseLocale ? JSON.parse(preParseLocale) : null;
|
||||||
export const version = _VERSION_;
|
export const version = _VERSION_;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import path from 'path';
|
||||||
import pluginReplace from '@rollup/plugin-replace';
|
import pluginReplace from '@rollup/plugin-replace';
|
||||||
import pluginVue from '@vitejs/plugin-vue';
|
import pluginVue from '@vitejs/plugin-vue';
|
||||||
import { type UserConfig, defineConfig } from 'vite';
|
import { type UserConfig, defineConfig } from 'vite';
|
||||||
|
import { localesVersion } from '../../locales/version.js';
|
||||||
import locales from '../../locales/index.js';
|
import locales from '../../locales/index.js';
|
||||||
import meta from '../../package.json';
|
import meta from '../../package.json';
|
||||||
import packageInfo from './package.json' with { type: 'json' };
|
import packageInfo from './package.json' with { type: 'json' };
|
||||||
|
@ -110,6 +110,7 @@ export function getConfig(): UserConfig {
|
||||||
define: {
|
define: {
|
||||||
_VERSION_: JSON.stringify(meta.version),
|
_VERSION_: JSON.stringify(meta.version),
|
||||||
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
|
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]) => [k, v._lang_])),
|
||||||
|
_LANGS_VERSION_: JSON.stringify(localesVersion),
|
||||||
_ENV_: JSON.stringify(process.env.NODE_ENV),
|
_ENV_: JSON.stringify(process.env.NODE_ENV),
|
||||||
_DEV_: process.env.NODE_ENV !== 'production',
|
_DEV_: process.env.NODE_ENV !== 'production',
|
||||||
_PERF_PREFIX_: JSON.stringify('Misskey:'),
|
_PERF_PREFIX_: JSON.stringify('Misskey:'),
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { build as buildLocales } from '../locales/index.js';
|
||||||
import generateDTS from '../locales/generateDTS.js';
|
import generateDTS from '../locales/generateDTS.js';
|
||||||
import meta from '../package.json' with { type: "json" };
|
import meta from '../package.json' with { type: "json" };
|
||||||
import buildTarball from './tarball.mjs';
|
import buildTarball from './tarball.mjs';
|
||||||
|
import { localesVersion } from '../locales/version.js';
|
||||||
|
|
||||||
const configDir = fileURLToPath(new URL('../.config', import.meta.url));
|
const configDir = fileURLToPath(new URL('../.config', import.meta.url));
|
||||||
const configPath = process.env.MISSKEY_CONFIG_YML
|
const configPath = process.env.MISSKEY_CONFIG_YML
|
||||||
|
@ -56,10 +57,10 @@ async function copyFrontendLocales() {
|
||||||
|
|
||||||
await fs.mkdir('./built/_frontend_dist_/locales', { recursive: true });
|
await fs.mkdir('./built/_frontend_dist_/locales', { recursive: true });
|
||||||
|
|
||||||
const v = { '_version_': meta.version };
|
const v = { '_version_': localesVersion };
|
||||||
|
|
||||||
for (const [lang, locale] of Object.entries(locales)) {
|
for (const [lang, locale] of Object.entries(locales)) {
|
||||||
await fs.writeFile(`./built/_frontend_dist_/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
|
await fs.writeFile(`./built/_frontend_dist_/locales/${lang}.${localesVersion}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +77,8 @@ async function buildBackendScript() {
|
||||||
'./packages/backend/src/server/web/cli.js'
|
'./packages/backend/src/server/web/cli.js'
|
||||||
]) {
|
]) {
|
||||||
let source = await fs.readFile(file, { encoding: 'utf-8' });
|
let source = await fs.readFile(file, { encoding: 'utf-8' });
|
||||||
source = source.replaceAll('LANGS', JSON.stringify(Object.keys(locales)));
|
source = source.replaceAll(/\bLANGS\b/g, JSON.stringify(Object.keys(locales)));
|
||||||
|
source = source.replaceAll(/\bLANGS_VERSION\b/g, JSON.stringify(localesVersion));
|
||||||
const { code } = await terser.minify(source, { toplevel: true });
|
const { code } = await terser.minify(source, { toplevel: true });
|
||||||
await fs.writeFile(`./packages/backend/built/server/web/${path.basename(file)}`, code);
|
await fs.writeFile(`./packages/backend/built/server/web/${path.basename(file)}`, code);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue