diff --git a/gulpfile.ts b/gulpfile.ts index 49a80879d..607f77fc3 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -20,7 +20,7 @@ import * as replace from 'gulp-replace'; import * as htmlmin from 'gulp-htmlmin'; const uglifyes = require('uglify-es'); -import locales from './locales'; +const locales = require('./locales'); import { fa } from './src/build/fa'; const client = require('./built/client/meta.json'); import config from './src/config'; diff --git a/locales/index.js b/locales/index.js new file mode 100644 index 000000000..95cc33916 --- /dev/null +++ b/locales/index.js @@ -0,0 +1,27 @@ +/** + * Languages Loader + */ + +const fs = require('fs'); +const yaml = require('js-yaml'); + +const loadLang = lang => yaml.safeLoad( + fs.readFileSync(`./locales/${lang}.yml`, 'utf-8')); + +const native = loadLang('ja'); + +const langs = { + 'de': loadLang('de'), + 'en': loadLang('en'), + 'fr': loadLang('fr'), + 'ja': native, + 'pl': loadLang('pl'), + 'es': loadLang('es') +}; + +Object.entries(langs).map(([, locale]) => { + // Extend native language (Japanese) + locale = Object.assign({}, native, locale); +}); + +module.exports = langs; diff --git a/locales/index.ts b/locales/index.ts deleted file mode 100644 index 45b5df095..000000000 --- a/locales/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Languages Loader - */ - -import * as fs from 'fs'; -import * as yaml from 'js-yaml'; - -export type LangKey = 'de' | 'en' | 'fr' | 'ja' | 'pl' | 'es'; -export type LocaleObject = { [key: string]: any }; - -const loadLang = (lang: LangKey) => yaml.safeLoad( - fs.readFileSync(`./locales/${lang}.yml`, 'utf-8')) as LocaleObject; - -const native = loadLang('ja'); - -const langs: { [key: string]: LocaleObject } = { - 'de': loadLang('de'), - 'en': loadLang('en'), - 'fr': loadLang('fr'), - 'ja': native, - 'pl': loadLang('pl'), - 'es': loadLang('es') -}; - -Object.entries(langs).map(([, locale]) => { - // Extend native language (Japanese) - locale = Object.assign({}, native, locale); -}); - -export function isAvailableLanguage(lang: string): lang is LangKey { - return lang in langs; -} - -export default langs; diff --git a/src/build/i18n.ts b/src/build/i18n.ts index dc48251c9..4ed3c7a2d 100644 --- a/src/build/i18n.ts +++ b/src/build/i18n.ts @@ -2,7 +2,7 @@ * Replace i18n texts */ -import locale, { isAvailableLanguage, LocaleObject } from '../../locales'; +const locale = require('../../locales'); export default class Replacer { private lang: string; @@ -16,8 +16,8 @@ export default class Replacer { this.replacement = this.replacement.bind(this); } - private get(path: string, key: string): string { - if (!isAvailableLanguage(this.lang)) { + public get(path: string, key: string): string { + if (!(this.lang in locale)) { console.warn(`lang '${this.lang}' is not supported`); return key; // Fallback } @@ -28,7 +28,7 @@ export default class Replacer { if (path) { if (text.hasOwnProperty(path)) { - text = text[path] as LocaleObject; + text = text[path]; } else { console.warn(`path '${path}' not found in '${this.lang}'`); return key; // Fallback @@ -38,7 +38,7 @@ export default class Replacer { // Check the key existance const error = key.split('.').some(k => { if (text.hasOwnProperty(k)) { - text = (text as LocaleObject)[k]; + text = text[k]; return false; } else { return true; diff --git a/src/client/docs/api/endpoints/view.pug b/src/client/docs/api/endpoints/view.pug index f8795c844..24fff1b79 100644 --- a/src/client/docs/api/endpoints/view.pug +++ b/src/client/docs/api/endpoints/view.pug @@ -17,7 +17,7 @@ block main p#desc= desc[lang] || desc['ja'] section - h2 %i18n:docs.api.endpoints.params% + h2= i18n('docs.api.endpoints.params') +propTable(params) if paramDefs @@ -28,5 +28,5 @@ block main if res section - h2 %i18n:docs.api.endpoints.res% + h2= i18n('docs.api.endpoints.res') +propTable(res) diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts deleted file mode 100644 index 4b38058b5..000000000 --- a/src/client/docs/api/gulpfile.ts +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Gulp tasks - */ - -import * as fs from 'fs'; -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 locales from '../../../../locales'; -import I18nReplacer from '../../../build/i18n'; -import fa from '../../../build/fa'; -import config from './../../../config'; - -import generateVars from '../vars'; -import { Context } from 'cafy'; -import ObjectContext from 'cafy/built/types/object'; - -const langs = Object.keys(locales); - -const kebab = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(); - -const parseParam = (param: any) => { - const id = param.type.match(/^id\((.+?)\)|^id/); - const entity = param.type.match(/^entity\((.+?)\)/); - const isObject = /^object/.test(param.type); - const isDate = /^date/.test(param.type); - const isArray = /\[\]$/.test(param.type); - if (id) { - param.kind = 'id'; - param.type = 'string'; - param.entity = id[1]; - if (isArray) { - param.type += '[]'; - } - } - if (entity) { - param.kind = 'entity'; - param.type = 'object'; - param.entity = entity[1]; - if (isArray) { - param.type += '[]'; - } - } - if (isObject) { - param.kind = 'object'; - } - if (isDate) { - param.kind = 'date'; - param.type = 'string'; - if (isArray) { - param.type += '[]'; - } - } - - return param; -}; - -// WIP type -const parseEPDefParam = (key: string, param: Context) => { - return Object.assign({ - name: key, - type: param.getType() - }, param.data); -}; - -const sortParams = (params: Array<{name: string}>) => { - params.sort((a, b) => { - if (a.name < b.name) - return -1; - if (a.name > b.name) - return 1; - return 0; - }); - return params; -}; - -// WIP type -const extractDefs = (params: Context[]) => { - let defs: any[] = []; - - params.forEach(param => { - if (param.data && param.data.ref) { - const props = (param as ObjectContext).props; - defs.push({ - name: param.data.ref, - params: sortParams(Object.keys(props).map(k => parseEPDefParam(k, props[k]))) - }); - - const childDefs = extractDefs(Object.keys(props).map(k => props[k])); - - defs = defs.concat(childDefs); - } - }); - - return sortParams(defs); -}; - -gulp.task('doc:api', [ - 'doc:api:endpoints', - 'doc:api:entities' -]); - -gulp.task('doc:api:endpoints', ['build:ts'], async () => { - const commonVars = await generateVars(); - glob('./built/server/api/endpoints/**/*.js', (globErr, files) => { - if (globErr) { - console.error(globErr); - return; - } - console.log(files.map(file => require('../../../../' + file))); - - files.map(file => require('../../../../' + file)).filter(x => x.meta).map(x => x.meta).forEach(ep => { - console.log(ep); - const vars = { - endpoint: ep.name, - url: { - host: config.api_url, - path: ep.name - }, - desc: ep.desc, - // @ts-ignore - params: sortParams(ep.params.map(p => parseEPDefParam(p))), - paramDefs: extractDefs(ep.params), - }; - langs.forEach(lang => { - pug.renderFile('./src/client/docs/api/endpoints/view.pug', Object.assign({}, vars, { - lang, - title: ep.name, - src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/api/endpoints/${ep.name}.yaml`, - kebab, - common: commonVars - }), (renderErr, html) => { - if (renderErr) { - console.error(renderErr); - return; - } - const i18n = new I18nReplacer(lang); - html = html.replace(i18n.pattern, i18n.replacement); - html = fa(html); - const htmlPath = `./built/client/docs/${lang}/api/endpoints/${ep.name}.html`; - mkdirp(path.dirname(htmlPath), (mkdirErr) => { - if (mkdirErr) { - console.error(mkdirErr); - return; - } - fs.writeFileSync(htmlPath, html, 'utf-8'); - }); - }); - }); - }); - }); -}); - -gulp.task('doc:api:entities', async () => { - const commonVars = await generateVars(); - glob('./src/client/docs/api/entities/**/*.yaml', (globErr, files) => { - if (globErr) { - console.error(globErr); - return; - } - files.forEach(file => { - const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8')) as any; - const vars = { - name: entity.name, - desc: entity.desc, - // WIP type - props: sortParams(entity.props.map((p: any) => parseParam(p))), - propDefs: extractDefs(entity.props), - }; - langs.forEach(lang => { - pug.renderFile('./src/client/docs/api/entities/view.pug', Object.assign({}, vars, { - lang, - title: entity.name, - src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/api/entities/${kebab(entity.name)}.yaml`, - kebab, - common: commonVars - }), (renderErr, html) => { - if (renderErr) { - console.error(renderErr); - return; - } - const i18n = new I18nReplacer(lang); - html = html.replace(i18n.pattern, i18n.replacement); - html = fa(html); - const htmlPath = `./built/client/docs/${lang}/api/entities/${kebab(entity.name)}.html`; - mkdirp(path.dirname(htmlPath), (mkdirErr) => { - if (mkdirErr) { - console.error(mkdirErr); - return; - } - fs.writeFileSync(htmlPath, html, 'utf-8'); - }); - }); - }); - }); - }); -}); diff --git a/src/client/docs/api/mixins.pug b/src/client/docs/api/mixins.pug index 913135a85..6762158f8 100644 --- a/src/client/docs/api/mixins.pug +++ b/src/client/docs/api/mixins.pug @@ -1,10 +1,10 @@ mixin propTable(props) table.props thead: tr - th %i18n:docs.api.props.name% - th %i18n:docs.api.props.type% - th %i18n:docs.api.props.optional% - th %i18n: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 - | %i18n:docs.api.props.yes% + = i18n('docs.api.props.yes') else - | %i18n:docs.api.props.no% - td.desc!= prop.desc[lang] || prop.desc['ja'] + = i18n('docs.api.props.no') + td.desc!= prop.desc ? prop.desc[lang] || prop.desc['ja'] : null diff --git a/src/client/docs/gulpfile.ts b/src/client/docs/gulpfile.ts index 5ef87315f..2a95dfbfe 100644 --- a/src/client/docs/gulpfile.ts +++ b/src/client/docs/gulpfile.ts @@ -2,73 +2,14 @@ * Gulp tasks */ -import * as fs from 'fs'; -import * as path from 'path'; -import * as glob from 'glob'; import * as gulp from 'gulp'; -import * as pug from 'pug'; -import * as mkdirp from 'mkdirp'; const stylus = require('gulp-stylus'); const cssnano = require('gulp-cssnano'); -import I18nReplacer from '../../build/i18n'; -import fa from '../../build/fa'; -import generateVars from './vars'; - -//require('./api/gulpfile.ts'); - gulp.task('doc', [ - 'doc:docs', - //'doc:api', 'doc:styles' ]); -gulp.task('doc:docs', async () => { - const commonVars = await generateVars(); - - glob('./src/client/docs/**/*.*.pug', (globErr, files) => { - if (globErr) { - console.error(globErr); - return; - } - files.forEach(file => { - const [, name, lang] = file.match(/docs\/(.+?)\.(.+?)\.pug$/); - const vars = { - common: commonVars, - lang: lang, - title: fs.readFileSync(file, 'utf-8').match(/^h1 (.+?)\r?\n/)[1], - src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/${name}.${lang}.pug`, - }; - pug.renderFile(file, vars, (renderErr, content) => { - if (renderErr) { - console.error(renderErr); - return; - } - - pug.renderFile('./src/client/docs/layout.pug', Object.assign({}, vars, { - content - }), (renderErr2, html) => { - if (renderErr2) { - console.error(renderErr2); - return; - } - const i18n = new I18nReplacer(lang); - html = html.replace(i18n.pattern, i18n.replacement); - html = fa(html); - const htmlPath = `./built/client/docs/${lang}/${name}.html`; - mkdirp(path.dirname(htmlPath), (mkdirErr) => { - if (mkdirErr) { - console.error(mkdirErr); - return; - } - fs.writeFileSync(htmlPath, html, 'utf-8'); - }); - }); - }); - }); - }); -}); - gulp.task('doc:styles', () => gulp.src('./src/client/docs/**/*.styl') .pipe(stylus()) diff --git a/src/server/web/docs.ts b/src/server/web/docs.ts index 849194388..b1916abc1 100644 --- a/src/server/web/docs.ts +++ b/src/server/web/docs.ts @@ -9,6 +9,7 @@ import { Context } from 'cafy'; import ObjectContext from 'cafy/built/types/object'; import config from '../../config'; import generateVars from '../../client/docs/vars'; +import I18n from '../../build/i18n'; const docs = `${__dirname}/../../client/docs/`; @@ -63,6 +64,7 @@ router.get('/assets/*', async ctx => { }); router.get('/*/api/endpoints/*', async ctx => { + const lang = ctx.params[0]; const ep = require('../../../built/server/api/endpoints/' + ctx.params[1]).meta; const vars = { @@ -76,14 +78,16 @@ router.get('/*/api/endpoints/*', async ctx => { params: sortParams(Object.keys(ep.params).map(k => parseEPDefParam(k, ep.params[k]))), paramDefs: extractDefs(Object.keys(ep.params).map(k => ep.params[k])), }; - console.log(vars); const commonVars = await generateVars(); + const i18n = new I18n(lang); + await ctx.render('../../../../src/client/docs/api/endpoints/view', Object.assign({}, vars, { - lang: 'ja', + lang, title: ep.name, kebab: (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(), + i18n: (key: string) => i18n.get(null, key), common: commonVars })); }); diff --git a/webpack.config.ts b/webpack.config.ts index 3c426ebb4..1e4904376 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -19,7 +19,7 @@ const constants = require('./src/const.json'); import config from './src/config'; import { licenseHtml } from './src/build/license'; -import locales from './locales'; +const locales = require('./locales'); const meta = require('./package.json'); const version = meta.clientVersion; const codename = meta.codename;