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

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
*/
import * as fs from 'fs';
import * as gulp from 'gulp';
import * as ts from 'gulp-typescript';
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.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', () => {
return gulp.src(['./src/server/web/boot.js'])
.pipe(replace('VERSION', JSON.stringify(meta.version)))
@ -47,7 +60,7 @@ gulp.task('build:client:style', () => {
.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([
'./src/emojilist.json',
'./src/server/web/views/**/*',

View File

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

View File

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

View File

@ -6,9 +6,9 @@ export const hostname = address.hostname;
export const url = address.origin;
export const apiUrl = url + '/api';
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 locale = _LOCALE_; // TODO: code splittingするため、翻訳ファイルを分割したうえでwebpackのimport alias使って読み込むようにしたい
export const locale = JSON.parse(localStorage.getItem('locale'));
export const version = _VERSION_;
export const instanceName = siteName === 'Misskey' ? host : siteName;
export const ui = localStorage.getItem('ui');

View File

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

View File

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