feat(client): 翻訳をIndexedDBに保存・プッシュ通知を翻訳 (#6396)

* wip

* tabun ok

* better msg

* oops

* fix lint

* Update gulpfile.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* Update src/client/scripts/set-i18n-contexts.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* refactor

Co-authored-by: acid-chicken <root@acid-chicken.com>

* 

* wip

* fix lint

* たぶんおk

* fix flush

* Translate Notification

* remove console.log

* fix

* add notifications

* remove san

* wip

* ok

* ✌️

* Update src/prelude/array.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* wip

* i18n refactor

* Update init.ts

* ✌️

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
This commit is contained in:
tamaina 2020-05-23 13:19:31 +09:00 committed by GitHub
parent 11141c878c
commit 3963ed8ff7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
143 changed files with 290 additions and 433 deletions

View file

@ -11,7 +11,7 @@ const cleanCSS = require('gulp-clean-css');
const sass = require('gulp-dart-sass');
const fiber = require('fibers');
const locales = require('./locales');
const locales: { [x: string]: any } = require('./locales');
const meta = require('./package.json');
gulp.task('build:ts', () => {
@ -31,8 +31,10 @@ gulp.task('build:copy:views', () =>
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), 'utf-8');
fs.writeFileSync(`./built/client/assets/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
}
cb();

View file

@ -507,6 +507,8 @@ addRelay: "リレーの追加"
inboxUrl: "inboxのURL"
addedRelays: "追加済みのリレー"
serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。"
deletedNote: "削除された投稿"
invisibleNote: "非公開の投稿"
_theme:
explore: "テーマを探す"
@ -1102,3 +1104,17 @@ _relayStatus:
requesting: "承認待ち"
accepted: "承認済み"
rejected: "拒否済み"
_notification:
fileUploaded: "ファイルがアップロードされました"
youGotMention: "{name}からのメンション"
youGotReply: "{name}からのリプライ"
youGotQuote: "{name}による引用"
youRenoted: "{name}がRenoteしました"
youGotPoll: "{name}が投票しました"
youGotMessagingMessageFromUser: "{name}からのチャットがあります"
youGotMessagingMessageFromGroup: "{name}のチャットがあります"
youWereFollowed: "フォローされました"
youReceivedFollowRequest: "フォローリクエストが来ました"
yourFollowRequestAccepted: "フォローリクエストが承認されました"
youWereInvitedToGroup: "グループに招待されました"

View file

@ -125,6 +125,7 @@
"css-loader": "3.5.3",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"deep-entries": "3.1.0",
"diskusage": "1.1.3",
"double-ended-queue": "2.1.0-0",
"escape-regexp": "0.0.1",
@ -151,6 +152,7 @@
"http-proxy-agent": "4.0.1",
"http-signature": "1.3.4",
"https-proxy-agent": "5.0.0",
"idb-keyval": "3.2.0",
"insert-text-at-cursor": "0.3.0",
"is-root": "2.1.0",
"is-svg": "4.2.1",

View file

@ -136,15 +136,12 @@ import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt,
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { ResizeObserver } from '@juggle/resize-observer';
import { v4 as uuid } from 'uuid';
import i18n from './i18n';
import { host, instanceName } from './config';
import { search } from './scripts/search';
const DESKTOP_THRESHOLD = 1100;
export default Vue.extend({
i18n,
components: {
XClock: () => import('./components/header-clock.vue').then(m => m.default),
MkButton: () => import('./components/ui/button.vue').then(m => m.default),

View file

@ -7,7 +7,6 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
type Captcha = {
render(container: string | Node, options: {
@ -31,7 +30,6 @@ declare global {
}
export default Vue.extend({
i18n,
props: {
provider: {
type: String,

View file

@ -7,13 +7,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { length } from 'stringz';
import { concat } from '../../prelude/array';
export default Vue.extend({
i18n,
props: {
value: {
type: Boolean,

View file

@ -15,11 +15,8 @@
<script lang="ts">
import Vue from 'vue';
import { faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
items: {
type: Array,

View file

@ -57,11 +57,8 @@ import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue';
import MkSignin from './signin.vue';
import parseAcct from '../../misc/acct/parse';
import i18n from '../i18n';
export default Vue.extend({
i18n,
components: {
MkButton,
MkInput,

View file

@ -12,13 +12,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XDrive from './drive.vue';
import XWindow from './window.vue';
export default Vue.extend({
i18n,
components: {
XDrive,
XWindow,

View file

@ -32,7 +32,6 @@
<script lang="ts">
import Vue from 'vue';
import { faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import copyToClipboard from '../scripts/copy-to-clipboard';
//import updateAvatar from '../api/update-avatar';
//import updateBanner from '../api/update-banner';
@ -40,8 +39,6 @@ import XFileThumbnail from './drive-file-thumbnail.vue';
import { faDownload, faLink, faICursor, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n,
components: {
XFileThumbnail
},

View file

@ -28,11 +28,8 @@
<script lang="ts">
import Vue from 'vue';
import { faFolder, faFolderOpen } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
folder: {
type: Object,

View file

@ -15,11 +15,8 @@
<script lang="ts">
import Vue from 'vue';
import { faCloud } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
folder: {
type: Object,

View file

@ -48,7 +48,6 @@
<script lang="ts">
import Vue from 'vue';
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import XNavFolder from './drive.nav-folder.vue';
import XFolder from './drive.folder.vue';
import XFile from './drive.file.vue';
@ -56,8 +55,6 @@ import XUploader from './uploader.vue';
import MkButton from './ui/button.vue';
export default Vue.extend({
i18n,
components: {
XNavFolder,
XFolder,

View file

@ -64,7 +64,6 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { emojilist } from '../../misc/emojilist';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faHistory, faUser } from '@fortawesome/free-solid-svg-icons';
@ -73,8 +72,6 @@ import { groupByX } from '../../prelude/array';
import XPopup from './popup.vue';
export default Vue.extend({
i18n,
components: {
XPopup,
},

View file

@ -9,11 +9,9 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import MkButton from './ui/button.vue';
export default Vue.extend({
i18n,
components: {
MkButton,
},

View file

@ -30,12 +30,9 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { faSpinner, faPlus, faMinus, faHourglassHalf } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n,
props: {
user: {
type: Object,

View file

@ -8,10 +8,8 @@
<script lang="ts">
import Vue from 'vue';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: ['q'],
data() {
return {

View file

@ -6,12 +6,9 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XModal from './modal.vue';
export default Vue.extend({
i18n,
components: {
XModal,
},

View file

@ -125,7 +125,6 @@
import Vue from 'vue';
import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import Chart from 'chart.js';
import i18n from '../i18n';
import MkSelect from './ui/select.vue';
const chartLimit = 90;
@ -140,8 +139,6 @@ const alpha = (hex, a) => {
};
export default Vue.extend({
i18n,
components: {
MkSelect
},

View file

@ -28,10 +28,8 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
media: {
type: Object,

View file

@ -21,12 +21,10 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
import ImageViewer from './image-viewer.vue';
export default Vue.extend({
i18n,
props: {
image: {
type: Object,

View file

@ -23,10 +23,8 @@
import Vue from 'vue';
import { faPlayCircle } from '@fortawesome/free-regular-svg-icons';
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
video: {
type: Object,

View file

@ -16,12 +16,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { toUnicode } from 'punycode';
import { host as localHost } from '../config';
export default Vue.extend({
i18n,
props: {
username: {
type: String,

View file

@ -93,7 +93,6 @@ import { faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, f
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array';
import i18n from '../i18n';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue';
@ -109,7 +108,6 @@ import { url } from '../config';
import copyToClipboard from '../scripts/copy-to-clipboard';
export default Vue.extend({
i18n,
components: {
XSub,

View file

@ -29,15 +29,12 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import paging from '../scripts/paging';
import XNote from './note.vue';
import XList from './date-separated-list.vue';
import MkButton from './ui/button.vue';
export default Vue.extend({
i18n,
components: {
XNote, XList, MkButton
},

View file

@ -61,13 +61,11 @@
import Vue from 'vue';
import { faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck, faPollH } from '@fortawesome/free-solid-svg-icons';
import { faClock } from '@fortawesome/free-regular-svg-icons';
import getNoteSummary from '../../misc/get-note-summary';
import noteSummary from '../../misc/get-note-summary';
import XReactionIcon from './reaction-icon.vue';
import MkFollowButton from './follow-button.vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
components: {
XReactionIcon, MkFollowButton
},
@ -89,7 +87,7 @@ export default Vue.extend({
},
data() {
return {
getNoteSummary,
getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]),
followRequestDone: false,
groupInviteDone: false,
faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck, faPollH

View file

@ -18,15 +18,12 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import paging from '../scripts/paging';
import XNotification from './notification.vue';
import XList from './date-separated-list.vue';
import XNote from './note.vue';
export default Vue.extend({
i18n,
components: {
XNotification,
XList,

View file

@ -8,13 +8,11 @@
<script lang="ts">
import Vue from 'vue';
import { faCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import MkTextarea from '../ui/textarea.vue';
import MkButton from '../ui/button.vue';
import { apiUrl } from '../../config';
export default Vue.extend({
i18n,
components: {
MkTextarea,
MkButton,

View file

@ -9,14 +9,11 @@ import Vue from 'vue';
import { parse } from '@syuilo/aiscript';
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
import { faHeart } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../i18n';
import XBlock from './page.block.vue';
import { Hpml } from '../../scripts/hpml/evaluator';
import { url } from '../../config';
export default Vue.extend({
i18n,
components: {
XBlock
},

View file

@ -51,7 +51,6 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import { erase } from '../../prelude/array';
import { addTime } from '../../prelude/time';
import { formatDateTimeString } from '../../misc/format-time-string';
@ -61,7 +60,6 @@ import MkSwitch from './ui/switch.vue';
import MkButton from './ui/button.vue';
export default Vue.extend({
i18n,
components: {
MkInput,
MkSelect,

View file

@ -24,11 +24,9 @@
<script lang="ts">
import Vue from 'vue';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import { sum } from '../../prelude/array';
export default Vue.extend({
i18n,
props: {
note: {
type: Object,

View file

@ -14,15 +14,12 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import * as XDraggable from 'vuedraggable';
import { faTimesCircle, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { faExclamationTriangle, faICursor } from '@fortawesome/free-solid-svg-icons';
import XFileThumbnail from './drive-file-thumbnail.vue'
export default Vue.extend({
i18n,
components: {
XDraggable,
XFileThumbnail

View file

@ -57,7 +57,6 @@ import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz';
import { toASCII } from 'punycode';
import i18n from '../i18n';
import MkVisibilityChooser from './visibility-chooser.vue';
import MkUserSelect from './user-select.vue';
import XNotePreview from './note-preview.vue';
@ -70,8 +69,6 @@ import { formatTimeString } from '../../misc/format-time-string';
import { selectDriveFile } from '../scripts/select-drive-file';
export default Vue.extend({
i18n,
components: {
XNotePreview,
XUploader: () => import('./uploader.vue').then(m => m.default),

View file

@ -4,9 +4,7 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
reaction: {
type: String,

View file

@ -11,14 +11,11 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { emojiRegex } from '../../misc/emoji-regex';
import XReactionIcon from './reaction-icon.vue';
import XPopup from './popup.vue';
export default Vue.extend({
i18n,
components: {
XPopup,
XReactionIcon,

View file

@ -20,10 +20,8 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
reaction: {
type: String,

View file

@ -5,10 +5,8 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
href: {
type: String,

View file

@ -7,13 +7,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XWindow from './window.vue';
import MkSignin from './signin.vue';
export default Vue.extend({
i18n,
components: {
MkSignin,
XWindow,

View file

@ -49,13 +49,10 @@ import { faLock, faGavel } from '@fortawesome/free-solid-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
import i18n from '../i18n';
import { apiUrl, host } from '../config';
import { byteify, hexify } from '../scripts/2fa';
export default Vue.extend({
i18n,
components: {
MkButton,
MkInput,

View file

@ -7,13 +7,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XWindow from './window.vue';
import XSignup from './signup.vue';
export default Vue.extend({
i18n,
components: {
XSignup,
XWindow,

View file

@ -53,15 +53,12 @@ import Vue from 'vue';
import { faLock, faExclamationTriangle, faSpinner, faCheck, faKey } from '@fortawesome/free-solid-svg-icons';
const getPasswordStrength = require('syuilo-password-strength');
import { toUnicode } from 'punycode';
import i18n from '../i18n';
import { host, url } from '../config';
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
import MkSwitch from './ui/switch.vue';
export default Vue.extend({
i18n,
components: {
MkButton,
MkInput,

View file

@ -10,10 +10,8 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
data() {
return {
hasDisconnected: false,

View file

@ -21,12 +21,10 @@
<script lang="ts">
import Vue from 'vue';
import { faReply } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import XPoll from './poll.vue';
import XMediaList from './media-list.vue';
export default Vue.extend({
i18n,
components: {
XPoll,
XMediaList,

View file

@ -8,10 +8,8 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
time: {
type: [Date, String],

View file

@ -21,13 +21,11 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { apiUrl } from '../config';
//import getMD5 from '../../scripts/get-md5';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n,
data() {
return {
uploads: [],

View file

@ -6,12 +6,9 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import MkUrlPreview from './url-preview.vue';
export default Vue.extend({
i18n,
components: {
MkUrlPreview
},

View file

@ -32,12 +32,9 @@
<script lang="ts">
import Vue from 'vue';
import { faPlayCircle } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import { url as local, lang } from '../config';
export default Vue.extend({
i18n,
props: {
url: {
type: String,

View file

@ -31,14 +31,11 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import paging from '../scripts/paging';
import MkContainer from './ui/container.vue';
import MkFollowButton from './follow-button.vue';
export default Vue.extend({
i18n,
components: {
MkContainer,
MkFollowButton,

View file

@ -6,15 +6,12 @@
import Vue from 'vue';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import XMenu from './menu.vue';
import copyToClipboard from '../scripts/copy-to-clipboard';
import { host } from '../config';
import getAcct from '../../misc/acct/render';
export default Vue.extend({
i18n,
components: {
XMenu
},

View file

@ -28,13 +28,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import parseAcct from '../../misc/acct/parse';
import MkFollowButton from './follow-button.vue';
export default Vue.extend({
i18n,
components: {
MkFollowButton
},

View file

@ -21,14 +21,11 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import { faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
import MkInput from './ui/input.vue';
import XWindow from './window.vue';
export default Vue.extend({
i18n,
components: {
MkInput,
XWindow,

View file

@ -31,13 +31,10 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import paging from '../scripts/paging';
import XModal from './modal.vue';
export default Vue.extend({
i18n,
components: {
XModal,
},

View file

@ -37,11 +37,9 @@
import Vue from 'vue';
import { faGlobe, faUnlock, faHome } from '@fortawesome/free-solid-svg-icons';
import { faEnvelope } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import XPopup from './popup.vue';
export default Vue.extend({
i18n,
components: {
XPopup
},

View file

@ -20,12 +20,9 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import XModal from './modal.vue';
export default Vue.extend({
i18n,
components: {
XModal,
},

View file

@ -1,3 +1,6 @@
import { clientDb, entries } from './db';
import { fromEntries } from '../prelude/array';
declare const _LANGS_: string[];
declare const _VERSION_: string;
declare const _ENV_: string;
@ -12,7 +15,7 @@ export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
export const lang = localStorage.getItem('lang');
export const langs = _LANGS_;
export const locale = JSON.parse(localStorage.getItem('locale'));
export const getLocale = async () => fromEntries((await entries(clientDb.i18n)) as [string, string][]);
export const version = _VERSION_;
export const env = _ENV_;
export const instanceName = siteName === 'Misskey' ? null : siteName;

68
src/client/db.ts Normal file
View file

@ -0,0 +1,68 @@
import { Store } from 'idb-keyval';
// Provide functions from idb-keyval
export { get, set, del, clear, keys } from 'idb-keyval';
//#region Construct DB
export const clientDb = {
i18n: new Store('MisskeyClient', 'i18n')
};
//#endregion
//#region Provide some tool functions
function openTransaction(store: Store, mode: IDBTransactionMode): Promise<IDBTransaction>{
return store._dbp.then(db => db.transaction(store.storeName, mode));
}
export function entries(store: Store): Promise<[IDBValidKey, unknown][]> {
const entries: [IDBValidKey, unknown][] = [];
return store._withIDBStore('readonly', store => {
store.openCursor().onsuccess = function () {
if (!this.result) return;
entries.push([this.result.key, this.result.value]);
this.result.continue();
};
}).then(() => entries);
}
export async function bulkGet(keys: IDBValidKey[], store: Store): Promise<[IDBValidKey, unknown][]> {
const valPromises: Promise<[IDBValidKey, unknown]>[] = [];
const tx = await openTransaction(store, 'readwrite');
const st = tx.objectStore(store.storeName);
for (const key of keys) {
valPromises.push(new Promise((resolve, reject) => {
const getting = st.get(key);
getting.onsuccess = function (e) {
return resolve([key, this.result]);
};
getting.onerror = function (e) {
return reject(this.error);
};
}));
}
return new Promise((resolve, reject) => {
tx.oncomplete = () => resolve(Promise.all(valPromises));
tx.abort = tx.onerror = () => reject(tx.error);
});
}
export async function bulkSet(map: [IDBValidKey, any][], store: Store): Promise<void> {
const tx = await openTransaction(store, 'readwrite');
const st = tx.objectStore(store.storeName);
for (const [key, value] of map) {
st.put(value, key);
}
return new Promise((resolve, reject) => {
tx.oncomplete = () => resolve();
tx.abort = tx.onerror = () => reject(tx.error);
});
}
export function count(store: Store): Promise<number> {
let req: IDBRequest<number>;
return store._withIDBStore('readonly', store => {
req = store.count();
}).then(() => req.result);
}
//#endregion

View file

@ -1,12 +0,0 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import { lang, locale } from './config';
Vue.use(VueI18n);
export default new VueI18n({
locale: lang,
messages: {
[lang]: locale
}
});

View file

@ -7,13 +7,13 @@ import Vuex from 'vuex';
import VueMeta from 'vue-meta';
import PortalVue from 'portal-vue';
import VAnimateCss from 'v-animate-css';
import VueI18n from 'vue-i18n';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import i18n from './i18n';
import VueHotkey from './scripts/hotkey';
import App from './app.vue';
import MiOS from './mios';
import { version, langs, instanceName } from './config';
import { version, langs, instanceName, getLocale } from './config';
import PostFormDialog from './components/post-form-dialog.vue';
import Dialog from './components/dialog.vue';
import Menu from './components/menu.vue';
@ -21,12 +21,15 @@ import { router } from './router';
import { applyTheme, lightTheme } from './theme';
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
import createStore from './store';
import { clientDb, get, count } from './db';
import { setI18nContexts } from './scripts/set-i18n-contexts';
Vue.use(Vuex);
Vue.use(VueHotkey);
Vue.use(VueMeta);
Vue.use(PortalVue);
Vue.use(VAnimateCss);
Vue.use(VueI18n);
Vue.component('fa', FontAwesomeIcon);
require('./directives');
@ -96,27 +99,6 @@ if (isMobile || window.innerWidth <= 1024) {
head.appendChild(viewport);
}
//#region Fetch locale data
const cachedLocale = localStorage.getItem('locale');
if (cachedLocale == null) {
fetch(`/assets/locales/${lang}.${version}.json`)
.then(response => response.json()).then(locale => {
localStorage.setItem('locale', JSON.stringify(locale));
i18n.locale = lang;
i18n.setLocaleMessage(lang, locale);
});
} else {
// TODO: 古い時だけ更新
setTimeout(() => {
fetch(`/assets/locales/${lang}.${version}.json`)
.then(response => response.json()).then(locale => {
localStorage.setItem('locale', JSON.stringify(locale));
});
}, 1000 * 5);
}
//#endregion
//#region Set lang attr
const html = document.documentElement;
html.setAttribute('lang', lang);
@ -167,6 +149,18 @@ os.init(async () => {
});
//#endregion
//#region Fetch locale data
const i18n = new VueI18n();
await count(clientDb.i18n).then(async n => {
if (n === 0) return setI18nContexts(lang, version, i18n);
if ((await get('_version_', clientDb.i18n) !== version)) return setI18nContexts(lang, version, i18n, true);
i18n.locale = lang;
i18n.setLocaleMessage(lang, await getLocale());
});
//#endregion
if ('Notification' in window && store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
@ -176,6 +170,7 @@ os.init(async () => {
const app = new Vue({
store: store,
i18n,
metaInfo: {
title: null,
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
@ -183,7 +178,8 @@ os.init(async () => {
data() {
return {
stream: os.stream,
isMobile: isMobile
isMobile: isMobile,
i18n // TODO: 消せないか考える SEE: https://github.com/syuilo/misskey/pull/6396#discussion_r429511030
};
},
methods: {

View file

@ -63,12 +63,9 @@
import Vue from 'vue';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { version } from '../config';
import i18n from '../i18n';
import MkLink from '../components/link.vue';
export default Vue.extend({
i18n,
components: {
MkLink
},

View file

@ -25,12 +25,9 @@
import Vue from 'vue';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { version } from '../config';
import i18n from '../i18n';
import MkInstanceStats from '../components/instance-stats.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('instance') as string

View file

@ -21,13 +21,10 @@
<script lang="ts">
import Vue from 'vue';
import { faCheck, faBroadcastTower } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import MkPagination from '../components/ui/pagination.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('announcements') as string

View file

@ -23,11 +23,9 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
i18n,
components: {
MkButton
},

View file

@ -30,12 +30,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XForm from './auth.form.vue';
import MkSignin from '../components/signin.vue';
export default Vue.extend({
i18n,
components: {
XForm,
MkSignin,

View file

@ -19,7 +19,6 @@ import Vue from 'vue';
import { faFileAlt } from '@fortawesome/free-solid-svg-icons'
import MarkdownIt from 'markdown-it';
import MarkdownItAnchor from 'markdown-it-anchor';
import i18n from '../i18n';
import { url, lang } from '../config';
import MkLink from '../components/link.vue';
@ -32,8 +31,6 @@ markdown.use(MarkdownItAnchor, {
});
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.title,

View file

@ -57,13 +57,10 @@
import Vue from 'vue';
import { faChartLine, faPlus, faHashtag, faRocket } from '@fortawesome/free-solid-svg-icons';
import { faBookmark, faCommentAlt } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import XUserList from '../components/user-list.vue';
import MkContainer from '../components/ui/container.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('explore') as string

View file

@ -5,11 +5,8 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
created() {
const acct = new URL(location.href).searchParams.get('acct');
if (acct == null) return;

View file

@ -20,12 +20,9 @@ import XSigninDialog from '../components/signin-dialog.vue';
import XSignupDialog from '../components/signup-dialog.vue';
import MkButton from '../components/ui/button.vue';
import XNotes from '../components/notes.vue';
import i18n from '../i18n';
import { host } from '../config';
export default Vue.extend({
i18n,
components: {
MkButton,
XNotes,

View file

@ -25,10 +25,8 @@ import { faLock } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../components/ui/button.vue';
import MkInput from '../components/ui/input.vue';
import { host } from '../config';
import i18n from '../i18n';
export default Vue.extend({
i18n,
components: {
MkButton,

View file

@ -28,14 +28,11 @@
import Vue from 'vue';
import { faBroadcastTower, faPlus } from '@fortawesome/free-solid-svg-icons';
import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkTextarea from '../../components/ui/textarea.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('announcements') as string

View file

@ -120,7 +120,6 @@
<script lang="ts">
import Vue from 'vue';
import Chart from 'chart.js';
import i18n from '../../i18n';
import { faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown, faMinusCircle, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import XWindow from '../../components/window.vue';
import MkUsersDialog from '../../components/users-dialog.vue';
@ -141,8 +140,6 @@ const alpha = hex => {
};
export default Vue.extend({
i18n,
components: {
XWindow,
MkSelect,

View file

@ -62,7 +62,6 @@
<script lang="ts">
import Vue from 'vue';
import { faGlobe, faCircle, faExchangeAlt, faCaretDown, faCaretUp, faTrafficLight } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkSelect from '../../components/ui/select.vue';
@ -70,8 +69,6 @@ import MkPagination from '../../components/ui/pagination.vue';
import MkInstanceInfo from './federation.instance.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('federation') as string

View file

@ -107,7 +107,6 @@ import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue';
import MkInput from '../../components/ui/input.vue';
import { version, url } from '../../config';
import i18n from '../../i18n';
const alpha = (hex, a) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!;
@ -118,8 +117,6 @@ const alpha = (hex, a) => {
};
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('instance') as string

View file

@ -25,7 +25,6 @@
<script lang="ts">
import Vue from 'vue';
import Chart from 'chart.js';
import i18n from '../../i18n';
const alpha = (hex, a) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!;
@ -36,8 +35,6 @@ const alpha = (hex, a) => {
};
export default Vue.extend({
i18n,
props: {
domain: {
required: true

View file

@ -21,13 +21,10 @@
import Vue from 'vue';
import { faExchangeAlt } from '@fortawesome/free-solid-svg-icons';
import { faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import XQueue from './queue.queue.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: `${this.$t('jobQueue')} | ${this.$t('instance')}`

View file

@ -28,13 +28,10 @@
import Vue from 'vue';
import { faPlus, faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('relays') as string

View file

@ -210,12 +210,9 @@ import MkSwitch from '../../components/ui/switch.vue';
import MkInfo from '../../components/ui/info.vue';
import MkUserSelect from '../../components/user-select.vue';
import { url } from '../../config';
import i18n from '../../i18n';
import getAcct from '../../../misc/acct/render';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('instance') as string

View file

@ -39,12 +39,9 @@ import { faTimes, faBookmark, faKey, faSync, faMicrophoneSlash, faExternalLinkSq
import { faSnowflake, faTrashAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue';
import i18n from '../../i18n';
import Progress from '../../scripts/loading';
export default Vue.extend({
i18n,
components: {
MkButton,
MkSwitch,

View file

@ -42,14 +42,11 @@
<script lang="ts">
import Vue from 'vue';
import { faUser, faUsers, faComments, faPlus } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import getAcct from '../../../misc/acct/render';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
i18n,
components: {
MkButton
},

View file

@ -27,12 +27,10 @@ import Vue from 'vue';
import { faPaperPlane, faPhotoVideo, faLaughSquint } from '@fortawesome/free-solid-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
import * as autosize from 'autosize';
import i18n from '../../i18n';
import { formatTimeString } from '../../../misc/format-time-string';
import { selectFile } from '../../scripts/select-file';
export default Vue.extend({
i18n,
components: {
XUploader: () => import('../../components/uploader.vue').then(m => m.default),
},

View file

@ -38,13 +38,11 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { parse } from '../../../mfm/parse';
import { unique } from '../../../prelude/array';
import MkUrlPreview from '../../components/url-preview.vue';
export default Vue.extend({
i18n,
components: {
MkUrlPreview
},

View file

@ -37,7 +37,6 @@
<script lang="ts">
import Vue from 'vue';
import { faArrowCircleDown, faFlag, faUsers, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import XList from '../../components/date-separated-list.vue';
import XMessage from './messaging-room.message.vue';
import XForm from './messaging-room.form.vue';
@ -45,8 +44,6 @@ import { url } from '../../config';
import parseAcct from '../../../misc/acct/parse';
export default Vue.extend({
i18n,
components: {
XMessage,
XForm,

View file

@ -40,12 +40,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import MkSignin from '../components/signin.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
i18n,
components: {
MkSignin,
MkButton,

View file

@ -48,7 +48,6 @@
<script lang="ts">
import Vue from 'vue';
import { faSave, faTrash } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkTextarea from '../../components/ui/textarea.vue';
@ -58,8 +57,6 @@ import MkUserSelect from '../../components/user-select.vue';
import getAcct from '../../../misc/acct/render';
export default Vue.extend({
i18n,
components: {
MkButton, MkInput, MkTextarea, MkSelect, MkSwitch
},

View file

@ -41,14 +41,11 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faUsers } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import Progress from '../../scripts/loading';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.group ? `${this.group.name} | ${this.$t('manageGroups')}` : this.$t('manageGroups')

View file

@ -40,14 +40,11 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faListUl } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import Progress from '../../scripts/loading';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.list ? `${this.list.name} | ${this.$t('manageLists')}` : this.$t('manageLists')

View file

@ -65,7 +65,6 @@
<script lang="ts">
import Vue from 'vue';
import { faLock } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import { hostname } from '../../config';
import { byteify, hexify, stringify } from '../../scripts/2fa';
import MkButton from '../../components/ui/button.vue';
@ -74,7 +73,6 @@ import MkInput from '../../components/ui/input.vue';
import MkSwitch from '../../components/ui/switch.vue';
export default Vue.extend({
i18n,
components: {
MkButton, MkInfo, MkInput, MkSwitch
},

View file

@ -13,12 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faKey, faSyncAlt } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
export default Vue.extend({
i18n,
components: {
MkButton, MkInput
},

View file

@ -13,12 +13,9 @@ import Vue from 'vue';
import { faCloud, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import { faClock, faEyeSlash, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import MkButton from '../../components/ui/button.vue';
import i18n from '../../i18n';
import { selectDriveFolder } from '../../scripts/select-drive-folder';
export default Vue.extend({
i18n,
components: {
MkButton,
},

View file

@ -21,12 +21,9 @@ import Vue from 'vue';
import { faDownload, faUpload, faBoxes } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue';
import i18n from '../../i18n';
import { apiUrl } from '../../config';
export default Vue.extend({
i18n,
components: {
MkButton,
MkSelect,

View file

@ -29,13 +29,10 @@
import Vue from 'vue';
import { faShareAlt } from '@fortawesome/free-solid-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
import i18n from '../../i18n';
import { apiUrl } from '../../config';
import MkButton from '../../components/ui/button.vue';
export default Vue.extend({
i18n,
components: {
MkButton
},

View file

@ -34,11 +34,8 @@
import Vue from 'vue';
import { faBan } from '@fortawesome/free-solid-svg-icons';
import MkPagination from '../../components/ui/pagination.vue';
import i18n from '../../i18n';
export default Vue.extend({
i18n,
components: {
MkPagination,
},

View file

@ -24,11 +24,8 @@ import Vue from 'vue';
import { faLock } from '@fortawesome/free-solid-svg-icons';
import MkSelect from '../../components/ui/select.vue';
import MkSwitch from '../../components/ui/switch.vue';
import i18n from '../../i18n';
export default Vue.extend({
i18n,
components: {
MkSelect,
MkSwitch,

View file

@ -62,13 +62,10 @@ import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkTextarea from '../../components/ui/textarea.vue';
import MkSwitch from '../../components/ui/switch.vue';
import i18n from '../../i18n';
import { host } from '../../config';
import { selectFile } from '../../scripts/select-file';
export default Vue.extend({
i18n,
components: {
MkButton,
MkInput,

View file

@ -21,13 +21,10 @@ import { faUndo } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue';
import MkReactionPicker from '../../components/reaction-picker.vue';
import i18n from '../../i18n';
import { emojiRegexWithCustom } from '../../../misc/emoji-regex';
import { defaultSettings } from '../../store';
export default Vue.extend({
i18n,
components: {
MkInput,
MkButton,

View file

@ -11,11 +11,8 @@
import Vue from 'vue';
import { faLock } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../../components/ui/button.vue';
import i18n from '../../i18n';
export default Vue.extend({
i18n,
components: {
MkButton,
},

View file

@ -15,11 +15,8 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('notFound') as string

View file

@ -29,14 +29,12 @@
<script lang="ts">
import Vue from 'vue';
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import Progress from '../scripts/loading';
import XNote from '../components/note.vue';
import XNotes from '../components/notes.vue';
import MkRemoteCaution from '../components/remote-caution.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('note') as string

View file

@ -40,15 +40,12 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkSelect from '../../../components/ui/select.vue';
import MkInput from '../../../components/ui/input.vue';
import MkSwitch from '../../../components/ui/switch.vue';
export default Vue.extend({
i18n,
components: {
XContainer, MkSelect, MkInput, MkSwitch
},

View file

@ -13,13 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faPaintBrush, faMagic } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
i18n,
components: {
XContainer, MkInput
},

View file

@ -13,13 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
i18n,
components: {
XContainer, MkInput
},

Some files were not shown because too many files have changed in this diff Show more