diff --git a/gulpfile.ts b/gulpfile.ts index 3b7a126407..21870473ed 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -20,16 +20,8 @@ import * as mocha from 'gulp-mocha'; import * as replace from 'gulp-replace'; import * as htmlmin from 'gulp-htmlmin'; const uglifyes = require('uglify-es'); -import * as fontawesome from '@fortawesome/fontawesome'; -import * as regular from '@fortawesome/fontawesome-free-regular'; -import * as solid from '@fortawesome/fontawesome-free-solid'; -import * as brands from '@fortawesome/fontawesome-free-brands'; - -// Add icons -fontawesome.library.add(regular); -fontawesome.library.add(solid); -fontawesome.library.add(brands); +import { fa } from './src/common/build/fa'; import version from './src/version'; import config from './src/conf'; @@ -179,7 +171,7 @@ gulp.task('build:client:pug', [ .pipe(pug({ locals: { themeColor: constants.themeColor, - facss: fontawesome.dom.css(), + facss: fa.dom.css(), //hljscss: fs.readFileSync('./node_modules/highlight.js/styles/default.css', 'utf8') hljscss: fs.readFileSync('./src/web/assets/code-highlight.css', 'utf8') } diff --git a/src/common/build/fa.ts b/src/common/build/fa.ts new file mode 100644 index 0000000000..0c21be9504 --- /dev/null +++ b/src/common/build/fa.ts @@ -0,0 +1,57 @@ +/** + * Replace fontawesome symbols + */ + +import * as fontawesome from '@fortawesome/fontawesome'; +import * as regular from '@fortawesome/fontawesome-free-regular'; +import * as solid from '@fortawesome/fontawesome-free-solid'; +import * as brands from '@fortawesome/fontawesome-free-brands'; + +// Add icons +fontawesome.library.add(regular); +fontawesome.library.add(solid); +fontawesome.library.add(brands); + +export const pattern = /%fa:(.+?)%/g; + +export const replacement = (_, key) => { + const args = key.split(' '); + let prefix = 'fas'; + const classes = []; + let transform = ''; + let name; + + args.forEach(arg => { + if (arg == 'R' || arg == 'S' || arg == 'B') { + prefix = + arg == 'R' ? 'far' : + arg == 'S' ? 'fas' : + arg == 'B' ? 'fab' : + ''; + } else if (arg[0] == '.') { + classes.push('fa-' + arg.substr(1)); + } else if (arg[0] == '-') { + transform = arg.substr(1).split('|').join(' '); + } else { + name = arg; + } + }); + + const icon = fontawesome.icon({ prefix, iconName: name }, { + classes: classes + }); + + if (icon) { + icon.transform = fontawesome.parse.transform(transform); + return `${icon.html[0]}`; + } else { + console.warn(`'${name}' not found in fa`); + return ''; + } +}; + +export default (src: string) => { + return src.replace(pattern, replacement); +}; + +export const fa = fontawesome; diff --git a/src/common/build/i18n.ts b/src/common/build/i18n.ts new file mode 100644 index 0000000000..1ae22147c4 --- /dev/null +++ b/src/common/build/i18n.ts @@ -0,0 +1,50 @@ +/** + * Replace i18n texts + */ + +import locale from '../../../locales'; + +export default class Replacer { + private lang: string; + + public pattern = /"%i18n:(.+?)%"|'%i18n:(.+?)%'|%i18n:(.+?)%/g; + + constructor(lang: string) { + this.lang = lang; + + this.get = this.get.bind(this); + this.replacement = this.replacement.bind(this); + } + + private get(key: string) { + let text = locale[this.lang]; + + // Check the key existance + const error = key.split('.').some(k => { + if (text.hasOwnProperty(k)) { + text = text[k]; + return false; + } else { + return true; + } + }); + + if (error) { + console.warn(`key '${key}' not found in '${this.lang}'`); + return key; // Fallback + } else { + return text; + } + } + + public replacement(match, a, b, c) { + const key = a || b || c; + if (match[0] == '"') { + return '"' + this.get(key).replace(/"/g, '\\"') + '"'; + } else if (match[0] == "'") { + return '\'' + this.get(key).replace(/'/g, '\\\'') + '\''; + } else { + return this.get(key); + } + } +} diff --git a/src/web/app/desktop/ui.styl b/src/web/app/desktop/ui.styl index cb98bf06a0..058271876b 100644 --- a/src/web/app/desktop/ui.styl +++ b/src/web/app/desktop/ui.styl @@ -1,4 +1,4 @@ -@import "../app" +@import "../../const" button font-family sans-serif diff --git a/src/web/const.styl b/src/web/const.styl new file mode 100644 index 0000000000..b6560701d9 --- /dev/null +++ b/src/web/const.styl @@ -0,0 +1,4 @@ +json('../const.json') + +$theme-color = themeColor +$theme-color-foreground = themeColorForeground diff --git a/src/web/docs/api.ja.pug b/src/web/docs/api.ja.pug index 5514a40975..2584b08581 100644 --- a/src/web/docs/api.ja.pug +++ b/src/web/docs/api.ja.pug @@ -7,6 +7,7 @@ section h2 自分の所有するアカウントからAPIにアクセスする場合 p 「設定 > API」で、APIにアクセスするのに必要なAPIキーを取得してください。 p APIにアクセスする際には、リクエストにAPIキーを「i」というパラメータ名で含めます。 + div.ui.info.warn: p %fa:exclamation-triangle%アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。 p APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。 section @@ -15,7 +16,7 @@ section | 直接ユーザーのAPIキーをアプリケーションが扱うのは危険なので、 | アプリケーションからAPIを利用する際には、アプリケーションとアプリケーションを利用するユーザーが結び付けられた専用のトークン(アクセストークン)をMisskeyに発行してもらい、 | そのトークンをリクエストのパラメータに含める必要があります。 - | (アクセストークンは、ユーザーが自分のアカウントにあなたのアプリケーションがアクセスすることを許可した場合のみ発行されます) + div.ui.info: p %fa:info-circle%アクセストークンは、ユーザーが自分のアカウントにあなたのアプリケーションがアクセスすることを許可した場合のみ発行されます p それでは、アクセストークンを取得するまでの流れを説明します。 @@ -46,9 +47,8 @@ section td 権限 td あなたのアプリケーションやWebサービスが要求する権限。ここで要求した機能だけがAPIからアクセスできます。 - p - | 登録が済むとアプリケーションのシークレットキーが入手できます。このシークレットキーは後で使用します。 - | アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。 + p 登録が済むとアプリケーションのシークレットキーが入手できます。このシークレットキーは後で使用します。 + div.ui.info.warn: p %fa:exclamation-triangle%アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。 section h3 2.ユーザーに認証させる @@ -93,7 +93,7 @@ section h2 Misskey APIの利用 p APIはすべてリクエストのパラメータ・レスポンスともにJSON形式です。また、すべてのエンドポイントはPOSTメソッドのみ受け付けます。 p APIリファレンスもご確認ください。 - + section h3 レートリミット p Misskey APIにはレートリミットがあり、短時間のうちに多数のリクエストを送信すると、一定時間APIを利用することができなくなることがあります。 diff --git a/src/web/docs/api/endpoints/view.pug b/src/web/docs/api/endpoints/view.pug index 90084ab276..d271a5517a 100644 --- a/src/web/docs/api/endpoints/view.pug +++ b/src/web/docs/api/endpoints/view.pug @@ -17,7 +17,7 @@ block main p#desc= desc[lang] || desc['ja'] section - h2= common.i18n[lang]['docs']['api']['endpoints']['params'] + h2 %i18n:docs.api.endpoints.params% +propTable(params) if paramDefs @@ -28,5 +28,5 @@ block main if res section - h2= common.i18n[lang]['docs']['api']['endpoints']['res'] + h2 %i18n:docs.api.endpoints.res% +propTable(res) diff --git a/src/web/docs/api/entities/view.pug b/src/web/docs/api/entities/view.pug index 99e786c694..2156463dc7 100644 --- a/src/web/docs/api/entities/view.pug +++ b/src/web/docs/api/entities/view.pug @@ -10,7 +10,7 @@ block main p#desc= desc[lang] || desc['ja'] section - h2= common.i18n[lang]['docs']['api']['entities']['properties'] + h2 %i18n:docs.api.entities.properties% +propTable(props) if propDefs diff --git a/src/web/docs/api/gulpfile.ts b/src/web/docs/api/gulpfile.ts index 2e8409c595..4c30871a0f 100644 --- a/src/web/docs/api/gulpfile.ts +++ b/src/web/docs/api/gulpfile.ts @@ -10,13 +10,16 @@ import * as pug from 'pug'; import * as yaml from 'js-yaml'; import * as mkdirp from 'mkdirp'; +import locales from '../../../../locales'; +import I18nReplacer from '../../../common/build/i18n'; +import fa from '../../../common/build/fa'; import config from './../../../conf'; import generateVars from '../vars'; const commonVars = generateVars(); -const langs = Object.keys(commonVars.i18n); +const langs = Object.keys(locales); const kebab = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(); @@ -124,6 +127,9 @@ gulp.task('doc:api:endpoints', () => { console.error(renderErr); return; } + const i18n = new I18nReplacer(lang); + html = html.replace(i18n.pattern, i18n.replacement); + html = fa(html); const htmlPath = `./built/web/docs/${lang}/api/endpoints/${ep.endpoint}.html`; mkdirp(path.dirname(htmlPath), (mkdirErr) => { if (mkdirErr) { @@ -164,6 +170,9 @@ gulp.task('doc:api:entities', () => { console.error(renderErr); return; } + const i18n = new I18nReplacer(lang); + html = html.replace(i18n.pattern, i18n.replacement); + html = fa(html); const htmlPath = `./built/web/docs/${lang}/api/entities/${kebab(entity.name)}.html`; mkdirp(path.dirname(htmlPath), (mkdirErr) => { if (mkdirErr) { diff --git a/src/web/docs/api/mixins.pug b/src/web/docs/api/mixins.pug index b563a121db..686bf6a2b6 100644 --- a/src/web/docs/api/mixins.pug +++ b/src/web/docs/api/mixins.pug @@ -1,10 +1,10 @@ mixin propTable(props) table.props thead: tr - th= common.i18n[lang]['docs']['api']['props']['name'] - th= common.i18n[lang]['docs']['api']['props']['type'] - th= common.i18n[lang]['docs']['api']['props']['optional'] - th= common.i18n[lang]['docs']['api']['props']['description'] + th %i18n:docs.api.props.name% + th %i18n:docs.api.props.type% + th %i18n:docs.api.props.optional% + th %i18n:docs.api.props.description% tbody each prop in props tr @@ -31,7 +31,7 @@ mixin propTable(props) | (Date) td.optional if prop.optional - = common.i18n[lang]['docs']['api']['props']['yes'] + | %i18n:docs.api.props.yes% else - = common.i18n[lang]['docs']['api']['props']['no'] + | %i18n:docs.api.props.no% td.desc!= prop.desc[lang] || prop.desc['ja'] diff --git a/src/web/docs/gulpfile.ts b/src/web/docs/gulpfile.ts index 6668abdec6..71033e1bc7 100644 --- a/src/web/docs/gulpfile.ts +++ b/src/web/docs/gulpfile.ts @@ -7,13 +7,12 @@ import * as path from 'path'; import * as glob from 'glob'; import * as gulp from 'gulp'; import * as pug from 'pug'; -//import * as yaml from 'js-yaml'; import * as mkdirp from 'mkdirp'; import stylus = require('gulp-stylus'); import cssnano = require('gulp-cssnano'); -//import config from './../../conf'; - +import I18nReplacer from '../../common/build/i18n'; +import fa from '../../common/build/fa'; import generateVars from './vars'; require('./api/gulpfile.ts'); @@ -53,6 +52,9 @@ gulp.task('doc:docs', () => { console.error(renderErr2); return; } + const i18n = new I18nReplacer(lang); + html = html.replace(i18n.pattern, i18n.replacement); + html = fa(html); const htmlPath = `./built/web/docs/${lang}/${name}.html`; mkdirp(path.dirname(htmlPath), (mkdirErr) => { if (mkdirErr) { diff --git a/src/web/docs/layout.pug b/src/web/docs/layout.pug index ee8018ec63..9dfd0ab7af 100644 --- a/src/web/docs/layout.pug +++ b/src/web/docs/layout.pug @@ -9,6 +9,9 @@ html(lang= lang) link(rel="stylesheet" href="/assets/style.css") block meta + //- FontAwesome style + style #{common.facss} + body nav ul @@ -33,6 +36,6 @@ html(lang= lang) footer p - = common.i18n[lang]['docs']['edit-this-page-on-github'] - a(href=src target="_blank")= common.i18n[lang]['docs']['edit-this-page-on-github-link'] + | %i18n:docs.edit-this-page-on-github% + a(href=src target="_blank") %i18n:docs.edit-this-page-on-github-link% small= common.copyright diff --git a/src/web/docs/style.styl b/src/web/docs/style.styl index 32a2264f15..414be5c53d 100644 --- a/src/web/docs/style.styl +++ b/src/web/docs/style.styl @@ -1,4 +1,5 @@ @import "../style" +@import "./ui" body margin 0 diff --git a/src/web/docs/ui.styl b/src/web/docs/ui.styl new file mode 100644 index 0000000000..8d5515712f --- /dev/null +++ b/src/web/docs/ui.styl @@ -0,0 +1,19 @@ +.ui.info + display block + margin 1em 0 + padding 0 1em + font-size 90% + color rgba(#000, 0.87) + background #f8f8f9 + border-radius 4px + overflow hidden + + > p + opacity 0.8 + + > [data-fa]:first-child + margin-right 0.25em + + &.warn + color #573a08 + background #FFFAF3 diff --git a/src/web/docs/vars.ts b/src/web/docs/vars.ts index da590d7bd9..65b224fbff 100644 --- a/src/web/docs/vars.ts +++ b/src/web/docs/vars.ts @@ -1,7 +1,8 @@ import * as fs from 'fs'; import * as glob from 'glob'; import * as yaml from 'js-yaml'; -import langs from '../../../locales'; + +import { fa } from '../../common/build/fa'; import config from '../../conf'; const constants = require('../../const.json'); @@ -37,9 +38,9 @@ export default function(): { [key: string]: any } { vars['config'] = config; - vars['i18n'] = langs; - vars['copyright'] = constants.copyright; + vars['facss'] = fa.dom.css(); + return vars; } diff --git a/src/web/style.styl b/src/web/style.styl index 573df10d78..c25fc8fb52 100644 --- a/src/web/style.styl +++ b/src/web/style.styl @@ -1,9 +1,6 @@ -json('../const.json') - @charset 'utf-8' -$theme-color = themeColor -$theme-color-foreground = themeColorForeground +@import "./const" /* ::selection diff --git a/webpack/module/index.ts b/webpack/module/index.ts index 15f36557ce..088aca7238 100644 --- a/webpack/module/index.ts +++ b/webpack/module/index.ts @@ -1,5 +1,5 @@ import rules from './rules'; -export default (lang, locale) => ({ - rules: rules(lang, locale) +export default lang => ({ + rules: rules(lang) }); diff --git a/webpack/module/rules/fa.ts b/webpack/module/rules/fa.ts index 47c72a28a1..891b78ece2 100644 --- a/webpack/module/rules/fa.ts +++ b/webpack/module/rules/fa.ts @@ -3,16 +3,7 @@ */ const StringReplacePlugin = require('string-replace-webpack-plugin'); - -import * as fontawesome from '@fortawesome/fontawesome'; -import * as regular from '@fortawesome/fontawesome-free-regular'; -import * as solid from '@fortawesome/fontawesome-free-solid'; -import * as brands from '@fortawesome/fontawesome-free-brands'; - -// Add icons -fontawesome.library.add(regular); -fontawesome.library.add(solid); -fontawesome.library.add(brands); +import { pattern, replacement } from '../../../src/common/build/fa'; export default () => ({ enforce: 'pre', @@ -20,41 +11,7 @@ export default () => ({ exclude: /node_modules/, loader: StringReplacePlugin.replace({ replacements: [{ - pattern: /%fa:(.+?)%/g, replacement: (_, key) => { - const args = key.split(' '); - let prefix = 'fas'; - const classes = []; - let transform = ''; - let name; - - args.forEach(arg => { - if (arg == 'R' || arg == 'S' || arg == 'B') { - prefix = - arg == 'R' ? 'far' : - arg == 'S' ? 'fas' : - arg == 'B' ? 'fab' : - ''; - } else if (arg[0] == '.') { - classes.push('fa-' + arg.substr(1)); - } else if (arg[0] == '-') { - transform = arg.substr(1).split('|').join(' '); - } else { - name = arg; - } - }); - - const icon = fontawesome.icon({ prefix, iconName: name }, { - classes: classes - }); - - if (icon) { - icon.transform = fontawesome.parse.transform(transform); - return `${icon.html[0]}`; - } else { - console.warn(`'${name}' not found in fa`); - return ''; - } - } + pattern, replacement }] }) }); diff --git a/webpack/module/rules/i18n.ts b/webpack/module/rules/i18n.ts index aa4e58448f..7261548be5 100644 --- a/webpack/module/rules/i18n.ts +++ b/webpack/module/rules/i18n.ts @@ -3,28 +3,10 @@ */ const StringReplacePlugin = require('string-replace-webpack-plugin'); +import Replacer from '../../../src/common/build/i18n'; -export default (lang, locale) => { - function get(key: string) { - let text = locale; - - // Check the key existance - const error = key.split('.').some(k => { - if (text.hasOwnProperty(k)) { - text = text[k]; - return false; - } else { - return true; - } - }); - - if (error) { - console.warn(`key '${key}' not found in '${lang}'`); - return key; // Fallback - } else { - return text; - } - } +export default lang => { + const replacer = new Replacer(lang); return { enforce: 'pre', @@ -32,14 +14,7 @@ export default (lang, locale) => { exclude: /node_modules/, loader: StringReplacePlugin.replace({ replacements: [{ - pattern: /"%i18n:(.+?)%"/g, replacement: (_, key) => - '"' + get(key).replace(/"/g, '\\"') + '"' - }, { - pattern: /'%i18n:(.+?)%'/g, replacement: (_, key) => - '\'' + get(key).replace(/'/g, '\\\'') + '\'' - }, { - pattern: /%i18n:(.+?)%/g, replacement: (_, key) => - get(key) + pattern: replacer.pattern, replacement: replacer.replacement }] }) }; diff --git a/webpack/module/rules/index.ts b/webpack/module/rules/index.ts index b6a0a5e2ec..b02bdef723 100644 --- a/webpack/module/rules/index.ts +++ b/webpack/module/rules/index.ts @@ -7,8 +7,8 @@ import tag from './tag'; import stylus from './stylus'; import typescript from './typescript'; -export default (lang, locale) => [ - i18n(lang, locale), +export default lang => [ + i18n(lang), license(), fa(), base64(), diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts index 124bd975b9..d67b8ef774 100644 --- a/webpack/webpack.config.ts +++ b/webpack/webpack.config.ts @@ -8,7 +8,7 @@ import plugins from './plugins'; import langs from '../locales'; import version from '../src/version'; -module.exports = Object.entries(langs).map(([lang, locale]) => { +module.exports = Object.keys(langs).map(lang => { // Chunk name const name = lang; @@ -32,7 +32,7 @@ module.exports = Object.entries(langs).map(([lang, locale]) => { return { name, entry, - module: module_(lang, locale), + module: module_(lang), plugins: plugins(version, lang), output, resolve: {