diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2020-12-26 10:01:32 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2020-12-26 10:01:32 +0900 |
| commit | 9d81d068533aaddf8e8654f9e86374c6531766bb (patch) | |
| tree | 2b6ff1106eff6431a2562e7e1fd8fd2531000045 /src/client | |
| parent | Showusers order by updateAt NULL considered as max (#7015) (diff) | |
| download | misskey-9d81d068533aaddf8e8654f9e86374c6531766bb.tar.gz misskey-9d81d068533aaddf8e8654f9e86374c6531766bb.tar.bz2 misskey-9d81d068533aaddf8e8654f9e86374c6531766bb.zip | |
wip
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/.eslintrc | 2 | ||||
| -rw-r--r-- | src/client/@types/global.d.ts | 4 | ||||
| -rw-r--r-- | src/client/components/abuse-report-window.vue | 4 | ||||
| -rw-r--r-- | src/client/components/channel-preview.vue | 8 | ||||
| -rw-r--r-- | src/client/components/global/i18n.ts | 15 | ||||
| -rw-r--r-- | src/client/components/index.ts | 2 | ||||
| -rw-r--r-- | src/client/components/note.vue | 8 | ||||
| -rw-r--r-- | src/client/components/signup.vue | 4 | ||||
| -rw-r--r-- | src/client/config.ts | 6 | ||||
| -rw-r--r-- | src/client/i18n.ts | 67 | ||||
| -rw-r--r-- | src/client/init.ts | 9 | ||||
| -rw-r--r-- | src/client/pages/channel.vue | 4 | ||||
| -rw-r--r-- | src/client/pages/reversi/game.setting.vue | 8 | ||||
| -rw-r--r-- | src/client/pages/reversi/index.vue | 4 | ||||
| -rw-r--r-- | src/client/pages/settings/2fa.vue | 4 | ||||
| -rw-r--r-- | src/client/pages/settings/general.vue | 4 | ||||
| -rw-r--r-- | src/client/pages/timeline.tutorial.vue | 8 | ||||
| -rw-r--r-- | src/client/scripts/set-i18n-contexts.ts | 15 | ||||
| -rw-r--r-- | src/client/ui/deck.vue | 2 |
19 files changed, 99 insertions, 79 deletions
diff --git a/src/client/.eslintrc b/src/client/.eslintrc index 8829472b49..5e309a95c1 100644 --- a/src/client/.eslintrc +++ b/src/client/.eslintrc @@ -1,7 +1,9 @@ { "globals": { "_DEV_": false, + "_LANG_": false, "_LANGS_": false, + "_LOCALE_": false, "_VERSION_": false, "_ENV_": false, "_PERF_PREFIX_": false, diff --git a/src/client/@types/global.d.ts b/src/client/@types/global.d.ts index 670774fdf4..a1ce02735a 100644 --- a/src/client/@types/global.d.ts +++ b/src/client/@types/global.d.ts @@ -1,4 +1,6 @@ -declare const _LANGS_: string[]; +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; diff --git a/src/client/components/abuse-report-window.vue b/src/client/components/abuse-report-window.vue index 1d87cb1802..c550e1e85d 100644 --- a/src/client/components/abuse-report-window.vue +++ b/src/client/components/abuse-report-window.vue @@ -2,11 +2,11 @@ <XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="$emit('closed')"> <template #header> <Fa :icon="faExclamationCircle" style="margin-right: 0.5em;"/> - <i18n-t keypath="reportAbuseOf" tag="span"> + <I18n src="reportAbuseOf" tag="span"> <template #name> <b><MkAcct :user="user"/></b> </template> - </i18n-t> + </I18n> </template> <div class="dpvffvvy"> <div class="_section"> diff --git a/src/client/components/channel-preview.vue b/src/client/components/channel-preview.vue index 41be1c296d..241fbe4f68 100644 --- a/src/client/components/channel-preview.vue +++ b/src/client/components/channel-preview.vue @@ -6,19 +6,19 @@ <div class="status"> <div> <Fa :icon="faUsers" fixed-width/> - <i18n-t keypath="_channel.usersCount" tag="span" style="margin-left: 4px;"> + <I18n src="_channel.usersCount" tag="span" style="margin-left: 4px;"> <template #n> <b>{{ channel.usersCount }}</b> </template> - </i18n-t> + </I18n> </div> <div> <Fa :icon="faPencilAlt" fixed-width/> - <i18n-t keypath="_channel.notesCount" tag="span" style="margin-left: 4px;"> + <I18n src="_channel.notesCount" tag="span" style="margin-left: 4px;"> <template #n> <b>{{ channel.notesCount }}</b> </template> - </i18n-t> + </I18n> </div> </div> </div> diff --git a/src/client/components/global/i18n.ts b/src/client/components/global/i18n.ts new file mode 100644 index 0000000000..603c07ca97 --- /dev/null +++ b/src/client/components/global/i18n.ts @@ -0,0 +1,15 @@ +import { h, Fragment, defineComponent } from 'vue'; +import type { SetupContext, VNodeChild, RenderFunction } from 'vue'; + +export default defineComponent({ + props: { + src: { + type: String, + required: true + }, + }, + render() { + // TODO + return h('span', this.src); + } +}); diff --git a/src/client/components/index.ts b/src/client/components/index.ts index cccb5d766a..0630ed3d8c 100644 --- a/src/client/components/index.ts +++ b/src/client/components/index.ts @@ -9,6 +9,7 @@ import userName from './global/user-name.vue'; import ellipsis from './global/ellipsis.vue'; import time from './global/time.vue'; import url from './global/url.vue'; +import i18n from './global/i18n'; import loading from './global/loading.vue'; import error from './global/error.vue'; @@ -24,4 +25,5 @@ export default function(app: App) { app.component('MkUrl', url); app.component('MkLoading', loading); app.component('MkError', error); + app.component('I18n', i18n); } diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 04696b7cfa..6d5750451e 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -16,13 +16,13 @@ <div class="renote" v-if="isRenote"> <MkAvatar class="avatar" :user="note.user"/> <Fa :icon="faRetweet"/> - <i18n-t keypath="renotedBy" tag="span"> + <I18n src="renotedBy" tag="span"> <template #user> <MkA class="name" :to="userPage(note.user)" v-user-preview="note.userId"> <MkUserName :user="note.user"/> </MkA> </template> - </i18n-t> + </I18n> <div class="info"> <button class="_button time" @click="showRenoteMenu()" ref="renoteTime"> <Fa class="dropdownIcon" v-if="isMyRenote" :icon="faEllipsisH"/> @@ -90,13 +90,13 @@ <XSub v-for="note in replies" :key="note.id" :note="note" class="reply" :detail="true"/> </div> <div v-else class="_panel muted" @click="muted = false"> - <i18n-t keypath="userSaysSomething" tag="small"> + <I18n src="userSaysSomething" tag="small"> <template #name> <MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId"> <MkUserName :user="appearNote.user"/> </MkA> </template> - </i18n-t> + </I18n> </div> </template> diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue index ec631c9429..5378ec38f4 100644 --- a/src/client/components/signup.vue +++ b/src/client/components/signup.vue @@ -38,9 +38,9 @@ </MkInput> <label v-if="meta.tosUrl" class="tou"> <input type="checkbox" v-model="ToSAgreement"> - <i18n-t keypath="agreeTo"> + <I18n src="agreeTo"> <a :href="meta.tosUrl" class="_link" target="_blank">{{ $t('tos') }}</a> - </i18n-t> + </I18n> </label> <captcha v-if="meta.enableHcaptcha" class="captcha" provider="hcaptcha" ref="hcaptcha" v-model:value="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/> <captcha v-if="meta.enableRecaptcha" class="captcha" provider="grecaptcha" ref="recaptcha" v-model:value="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/> diff --git a/src/client/config.ts b/src/client/config.ts index d0b74be042..b9cc361cf6 100644 --- a/src/client/config.ts +++ b/src/client/config.ts @@ -1,5 +1,3 @@ -import { clientDb, entries } from './db'; - const address = new URL(location.href); const siteName = (document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement)?.content; @@ -8,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 = localStorage.getItem('lang'); +export const lang = _LANG_; export const langs = _LANGS_; -export const getLocale = async () => Object.fromEntries((await entries(clientDb.i18n)) as [string, string][]); +export const locale = _LOCALE_; export const version = _VERSION_; export const instanceName = siteName === 'Misskey' ? host : siteName; export const ui = localStorage.getItem('ui'); diff --git a/src/client/i18n.ts b/src/client/i18n.ts index 6f2dd1a7d2..ee09c4da35 100644 --- a/src/client/i18n.ts +++ b/src/client/i18n.ts @@ -1,36 +1,49 @@ -import { createI18n } from 'vue-i18n'; -import { clientDb, get, count } from './db'; -import { setI18nContexts } from '@/scripts/set-i18n-contexts'; -import { version, langs, getLocale } from '@/config'; +import { markRaw } from 'vue'; +import { locale } from '@/config'; -let _lang = localStorage.getItem('lang'); +export class I18n<T extends Record<string, any>> { + public locale: T; -if (_lang == null) { - if (langs.map(x => x[0]).includes(navigator.language)) { - _lang = navigator.language; - } else { - _lang = langs.map(x => x[0]).find(x => x.split('-')[0] == navigator.language); + constructor(locale: T) { + this.locale = locale; - if (_lang == null) { - // Fallback - _lang = 'en-US'; + if (_DEV_) { + console.log('i18n', this.locale); } - } - localStorage.setItem('lang', _lang); -} + //#region BIND + this.t = this.t.bind(this); + //#endregion + } -export const lang = _lang; + // string にしているのは、ドット区切りでのパス指定を許可するため + // なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも + public t(key: string, args?: Record<string, any>): string { + try { + let str = key.split('.').reduce((o, i) => o[i], this.locale) as string; + if (args) { + for (const [k, v] of Object.entries(args)) { + str = str.replace(`{${k}}`, v); + } + } + return str; + } catch (e) { + if (_DEV_) { + console.warn(`missing localization '${key}'`); + return `⚠'${key}'⚠`; + } -export const locale = await count(clientDb.i18n).then(async n => { - if (n === 0) return await setI18nContexts(_lang, version); - if ((await get('_version_', clientDb.i18n) !== version)) return await setI18nContexts(_lang, version, true); + return key; + } + } +} - return await getLocale(); -}); +export const i18n = markRaw(new I18n(locale)); -export const i18n = createI18n({ - sync: false, - locale: _lang, - messages: { [_lang]: locale } -}); +// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + $t: typeof i18n['t']; + $ts: typeof i18n['locale']; + } +} diff --git a/src/client/init.ts b/src/client/init.ts index 609c1086a5..c5f147ca66 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -40,11 +40,11 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import widgets from '@/widgets'; import directives from '@/directives'; import components from '@/components'; -import { version, ui } from '@/config'; +import { version, ui, lang } from '@/config'; import { router } from '@/router'; import { applyTheme } from '@/scripts/theme'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; -import { i18n, lang } from '@/i18n'; +import { i18n } from '@/i18n'; import { stream, isMobile, dialog } from '@/os'; import * as sound from '@/scripts/sound'; import { $i, refreshAccount, login, updateAccount, signout } from '@/account'; @@ -53,6 +53,8 @@ import { fetchInstance, instance } from '@/instance'; console.info(`Misskey v${version}`); +window.clearTimeout(window.mkBootTimer); + if (_DEV_) { console.warn('Development mode!!!'); @@ -175,10 +177,11 @@ app.config.globalProperties = { $i, $store: defaultStore, $instance: instance, + $t: i18n.t, + $ts: i18n.locale, }; app.use(router); -app.use(i18n); // eslint-disable-next-line vue/component-definition-name-casing app.component('Fa', FontAwesomeIcon); diff --git a/src/client/pages/channel.vue b/src/client/pages/channel.vue index 790ad5230c..2610ab1002 100644 --- a/src/client/pages/channel.vue +++ b/src/client/pages/channel.vue @@ -10,8 +10,8 @@ </div> <div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner"> <div class="status"> - <div><Fa :icon="faUsers" fixed-width/><i18n-t keypath="_channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></i18n-t></div> - <div><Fa :icon="faPencilAlt" fixed-width/><i18n-t keypath="_channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></i18n-t></div> + <div><Fa :icon="faUsers" fixed-width/><I18n src="_channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div> + <div><Fa :icon="faPencilAlt" fixed-width/><I18n src="_channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div> </div> <div class="fade"></div> </div> diff --git a/src/client/pages/reversi/game.setting.vue b/src/client/pages/reversi/game.setting.vue index 0a98d880e7..f23a5816f2 100644 --- a/src/client/pages/reversi/game.setting.vue +++ b/src/client/pages/reversi/game.setting.vue @@ -35,18 +35,18 @@ <div> <MkRadio v-model="game.bw" value="random" @update:modelValue="updateSettings('bw')">{{ $t('random') }}</MkRadio> <MkRadio v-model="game.bw" :value="'1'" @update:modelValue="updateSettings('bw')"> - <i18n-t keypath="_reversi.blackIs" tag="span"> + <I18n src="_reversi.blackIs" tag="span"> <template #name> <b><MkUserName :user="game.user1"/></b> </template> - </i18n-t> + </I18n> </MkRadio> <MkRadio v-model="game.bw" :value="'2'" @update:modelValue="updateSettings('bw')"> - <i18n-t keypath="_reversi.blackIs" tag="span"> + <I18n src="_reversi.blackIs" tag="span"> <template #name> <b><MkUserName :user="game.user2"/></b> </template> - </i18n-t> + </I18n> </MkRadio> </div> </div> diff --git a/src/client/pages/reversi/index.vue b/src/client/pages/reversi/index.vue index 0ae0ad5a04..d57b9ae20d 100644 --- a/src/client/pages/reversi/index.vue +++ b/src/client/pages/reversi/index.vue @@ -46,11 +46,11 @@ </div> <div class="sazhgisb" v-else> <h1> - <i18n-t keypath="waitingFor" tag="span"> + <I18n src="waitingFor" tag="span"> <template #x> <b><MkUserName :user="matching"/></b> </template> - </i18n-t> + </I18n> <MkEllipsis/> </h1> <div class="cancel"> diff --git a/src/client/pages/settings/2fa.vue b/src/client/pages/settings/2fa.vue index 0e75382ee8..8d7a9a3832 100644 --- a/src/client/pages/settings/2fa.vue +++ b/src/client/pages/settings/2fa.vue @@ -45,14 +45,14 @@ <div v-if="data && !$i.twoFactorEnabled"> <ol style="margin: 0; padding: 0 0 0 1em;"> <li> - <i18n-t keypath="_2fa.step1" tag="span"> + <I18n src="_2fa.step1" tag="span"> <template #a> <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a> </template> <template #b> <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a> </template> - </i18n-t> + </I18n> </li> <li>{{ $t('_2fa.step2') }}<br><img :src="data.qr"></li> <li>{{ $t('_2fa.step3') }}<br> diff --git a/src/client/pages/settings/general.vue b/src/client/pages/settings/general.vue index 0151a29b5a..92622f4515 100644 --- a/src/client/pages/settings/general.vue +++ b/src/client/pages/settings/general.vue @@ -6,11 +6,11 @@ <template #label>{{ $t('uiLanguage') }}</template> <option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option> <template #caption> - <i18n-t keypath="i18nInfo" tag="span"> + <I18n src="i18nInfo" tag="span"> <template #link> <MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink> </template> - </i18n-t> + </I18n> </template> </FormSelect> diff --git a/src/client/pages/timeline.tutorial.vue b/src/client/pages/timeline.tutorial.vue index f3347c1463..60e71b0979 100644 --- a/src/client/pages/timeline.tutorial.vue +++ b/src/client/pages/timeline.tutorial.vue @@ -23,14 +23,14 @@ </div> <div class="_content" v-else-if="tutorial === 4"> <div>{{ $t('_tutorial.step5_1') }}</div> - <i18n-t keypath="_tutorial.step5_2" tag="div"> + <I18n src="_tutorial.step5_2" tag="div"> <template #featured> <MkA class="_link" to="/featured">{{ $t('featured') }}</MkA> </template> <template #explore> <MkA class="_link" to="/explore">{{ $t('explore') }}</MkA> </template> - </i18n-t> + </I18n> <div>{{ $t('_tutorial.step5_3') }}</div> <small>{{ $t('_tutorial.step5_4') }}</small> </div> @@ -41,11 +41,11 @@ </div> <div class="_content" v-else-if="tutorial === 6"> <div>{{ $t('_tutorial.step7_1') }}</div> - <i18n-t keypath="_tutorial.step7_2" tag="div"> + <I18n src="_tutorial.step7_2" tag="div"> <template #help> <MkA class="_link" to="/docs">{{ $t('help') }}</MkA> </template> - </i18n-t> + </I18n> <div>{{ $t('_tutorial.step7_3') }}</div> </div> diff --git a/src/client/scripts/set-i18n-contexts.ts b/src/client/scripts/set-i18n-contexts.ts deleted file mode 100644 index 6014957361..0000000000 --- a/src/client/scripts/set-i18n-contexts.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { clientDb, clear, bulkSet } from '../db'; -import { deepEntries, delimitEntry } from 'deep-entries'; - -export function setI18nContexts(lang: string, version: string, cleardb = false) { - return Promise.all([ - cleardb ? clear(clientDb.i18n) : Promise.resolve(), - fetch(`/assets/locales/${lang}.${version}.json`) - ]) - .then(([, response]) => response.json()) - .then(locale => { - const flatLocaleEntries = deepEntries(locale, delimitEntry) as [string, string][]; - bulkSet(flatLocaleEntries, clientDb.i18n); - return Object.fromEntries(flatLocaleEntries); - }); -} diff --git a/src/client/ui/deck.vue b/src/client/ui/deck.vue index 6d8fe4748e..a33e234107 100644 --- a/src/client/ui/deck.vue +++ b/src/client/ui/deck.vue @@ -177,7 +177,7 @@ export default defineComponent({ // TODO: この値を設定で変えられるようにする? $columnMargin: 12px; - $deckMargin: 12px; + $deckMargin: $columnMargin; --margin: var(--marginHalf); |