翻訳ファイルをランタイムで読み込み

This commit is contained in:
syuilo 2020-12-26 15:13:25 +09:00
parent ec4d5857d8
commit 84b488a912
6 changed files with 46 additions and 33 deletions

View file

@ -2,6 +2,7 @@
* Gulp tasks * Gulp tasks
*/ */
import * as fs from 'fs';
import * as gulp from 'gulp'; import * as gulp from 'gulp';
import * as ts from 'gulp-typescript'; import * as ts from 'gulp-typescript';
import * as rimraf from 'rimraf'; import * as rimraf from 'rimraf';
@ -31,6 +32,18 @@ gulp.task('build:copy:fonts', () =>
gulp.src('./node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/client/assets/fonts/')) gulp.src('./node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/client/assets/fonts/'))
); );
gulp.task('build:copy:locales', cb => {
fs.mkdirSync('./built/client/assets/locales', { recursive: true });
const v = { '_version_': meta.version };
for (const [lang, locale] of Object.entries(locales)) {
fs.writeFileSync(`./built/client/assets/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
}
cb();
});
gulp.task('build:client:script', () => { gulp.task('build:client:script', () => {
return gulp.src(['./src/server/web/boot.js']) return gulp.src(['./src/server/web/boot.js'])
.pipe(replace('VERSION', JSON.stringify(meta.version))) .pipe(replace('VERSION', JSON.stringify(meta.version)))
@ -47,7 +60,7 @@ gulp.task('build:client:style', () => {
.pipe(gulp.dest('./built/server/web/')); .pipe(gulp.dest('./built/server/web/'));
}); });
gulp.task('build:copy', gulp.parallel('build:copy:views', 'build:client:script', 'build:client:style', 'build:copy:fonts', () => gulp.task('build:copy', gulp.parallel('build:copy:locales', 'build:copy:views', 'build:client:script', 'build:client:style', 'build:copy:fonts', () =>
gulp.src([ gulp.src([
'./src/emojilist.json', './src/emojilist.json',
'./src/server/web/views/**/*', './src/server/web/views/**/*',

View file

@ -1,9 +1,7 @@
{ {
"globals": { "globals": {
"_DEV_": false, "_DEV_": false,
"_LANG_": false,
"_LANGS_": false, "_LANGS_": false,
"_LOCALE_": false,
"_VERSION_": false, "_VERSION_": false,
"_ENV_": false, "_ENV_": false,
"_PERF_PREFIX_": false, "_PERF_PREFIX_": false,

View file

@ -1,6 +1,4 @@
declare const _LANG_: string;
declare const _LANGS_: string[][]; declare const _LANGS_: string[][];
declare const _LOCALE_: Record<string, any>;
declare const _VERSION_: string; declare const _VERSION_: string;
declare const _ENV_: string; declare const _ENV_: string;
declare const _DEV_: boolean; declare const _DEV_: boolean;

View file

@ -6,9 +6,9 @@ export const hostname = address.hostname;
export const url = address.origin; export const url = address.origin;
export const apiUrl = url + '/api'; export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
export const lang = _LANG_; export const lang = localStorage.getItem('lang');
export const langs = _LANGS_; export const langs = _LANGS_;
export const locale = _LOCALE_; // TODO: code splittingするため、翻訳ファイルを分割したうえでwebpackのimport alias使って読み込むようにしたい export const locale = JSON.parse(localStorage.getItem('locale'));
export const version = _VERSION_; export const version = _VERSION_;
export const instanceName = siteName === 'Misskey' ? host : siteName; export const instanceName = siteName === 'Misskey' ? host : siteName;
export const ui = localStorage.getItem('ui'); export const ui = localStorage.getItem('ui');

View file

@ -1,7 +1,8 @@
/** /**
* BOOT LOADER * BOOT LOADER
* サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで以下の役割を持ちます * サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで以下の役割を持ちます
* - バージョンやユーザーの言語に基づいて適切なメインスクリプトを読み込む * - 翻訳ファイルをフェッチする
* - バージョンに基づいて適切なメインスクリプトを読み込む
* - キャッシュされたコンパイル済みテーマを適用する * - キャッシュされたコンパイル済みテーマを適用する
* - クライアントの設定値に基づいて対応するHTMLクラス等を設定する * - クライアントの設定値に基づいて対応するHTMLクラス等を設定する
* テーマをこの段階で設定するのはメインスクリプトが読み込まれる間もテーマを適用したいためです * テーマをこの段階で設定するのはメインスクリプトが読み込まれる間もテーマを適用したいためです
@ -10,27 +11,34 @@
'use strict'; 'use strict';
// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔 // ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
{ (async () => {
//#region Script const v = localStorage.getItem('v') || VERSION;
//#region Detect language //#region Detect language & fetch translations
const supportedLangs = LANGS; if (localStorage.hasOwnProperty('locale')) {
let lang = localStorage.getItem('lang'); // TODO: 非同期でlocaleの更新処理をする
if (lang == null || !supportedLangs.includes(lang)) { } else {
if (supportedLangs.includes(navigator.language)) { const supportedLangs = LANGS;
lang = navigator.language; let lang = localStorage.getItem('lang');
} else { if (lang == null || !supportedLangs.includes(lang)) {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); if (supportedLangs.includes(navigator.language)) {
lang = navigator.language;
} else {
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
// Fallback // Fallback
if (lang == null) lang = 'en-US'; if (lang == null) lang = 'en-US';
}
} }
const res = await fetch(`/assets/locales/${lang}.${v}.json`);
const json = await res.json();
localStorage.setItem('locale', JSON.stringify(json));
} }
//#endregion //#endregion
const ver = localStorage.getItem('v') || VERSION; //#region Script
const salt = localStorage.getItem('salt') const salt = localStorage.getItem('salt')
? `?salt=${localStorage.getItem('salt')}` ? `?salt=${localStorage.getItem('salt')}`
: ''; : '';
@ -38,7 +46,7 @@
const head = document.getElementsByTagName('head')[0]; const head = document.getElementsByTagName('head')[0];
const script = document.createElement('script'); const script = document.createElement('script');
script.setAttribute('src', `/assets/app.${ver}.${lang}.js${salt}`); script.setAttribute('src', `/assets/app.${v}.js${salt}`);
script.setAttribute('async', 'true'); script.setAttribute('async', 'true');
script.setAttribute('defer', 'true'); script.setAttribute('defer', 'true');
head.appendChild(script); head.appendChild(script);
@ -56,7 +64,7 @@
const meta = await res.json(); const meta = await res.json();
if (meta.version != ver) { if (meta.version != v) {
localStorage.setItem('v', meta.version); localStorage.setItem('v', meta.version);
alert( alert(
'Misskeyの新しいバージョンがあります。ページを再度読み込みします。' + 'Misskeyの新しいバージョンがあります。ページを再度読み込みします。' +
@ -113,4 +121,4 @@
location.reload(); location.reload();
} }
} })();

View file

@ -33,9 +33,7 @@ const postcss = {
}, },
}; };
module.exports = Object.keys(isProduction ? locales : { module.exports = {
'ja-JP': locales['ja-JP']
}).map(lang => ({
entry: { entry: {
app: './src/client/init.ts', app: './src/client/init.ts',
sw: './src/client/sw/sw.ts' sw: './src/client/sw/sw.ts'
@ -133,9 +131,7 @@ module.exports = Object.keys(isProduction ? locales : {
new webpack.ProgressPlugin({}), new webpack.ProgressPlugin({}),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
_VERSION_: JSON.stringify(meta.version), _VERSION_: JSON.stringify(meta.version),
_LANG_: JSON.stringify(lang),
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]: [string, any]) => [k, v._lang_])), _LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]: [string, any]) => [k, v._lang_])),
_LOCALE_: JSON.stringify(locales[lang]),
_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:'),
@ -153,7 +149,7 @@ module.exports = Object.keys(isProduction ? locales : {
], ],
output: { output: {
path: __dirname + '/built/client/assets', path: __dirname + '/built/client/assets',
filename: `[name].${meta.version}.${lang}.js`, filename: `[name].${meta.version}.js`,
publicPath: `/assets/` publicPath: `/assets/`
}, },
resolve: { resolve: {
@ -173,4 +169,4 @@ module.exports = Object.keys(isProduction ? locales : {
}, },
devtool: false, //'source-map', devtool: false, //'source-map',
mode: isProduction ? 'production' : 'development' mode: isProduction ? 'production' : 'development'
})); };