diff options
Diffstat (limited to 'packages/i18n/scripts/generateLocaleInterface.ts')
| -rw-r--r-- | packages/i18n/scripts/generateLocaleInterface.ts | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/packages/i18n/scripts/generateLocaleInterface.ts b/packages/i18n/scripts/generateLocaleInterface.ts new file mode 100644 index 0000000000..1c0f5c6a79 --- /dev/null +++ b/packages/i18n/scripts/generateLocaleInterface.ts @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as fs from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; +import * as yaml from 'js-yaml'; +import ts from 'typescript'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const parameterRegExp = /\{(\w+)\}/g; + +interface LocaleRecord { + [key: string]: string | LocaleRecord; +} + +function createMemberType(item: string | LocaleRecord): ts.TypeNode { + if (typeof item !== 'string') { + return ts.factory.createTypeLiteralNode(createMembers(item)); + } + const parameters = Array.from( + item.matchAll(parameterRegExp), + ([, parameter]) => parameter, + ); + return parameters.length + ? ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ParameterizedString'), + [ + ts.factory.createUnionTypeNode( + parameters.map((parameter) => + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(parameter)), + ), + ), + ], + ) + : ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); +} + +function createMembers(record: LocaleRecord): ts.TypeElement[] { + return Object.entries(record).map(([k, v]) => { + const node = ts.factory.createPropertySignature( + undefined, + ts.factory.createStringLiteral(k), + undefined, + createMemberType(v), + ); + if (typeof v === 'string') { + ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + `* + * ${v.replace(/\n/g, '\n * ')} + `, + true, + ); + } + return node; + }); +} + +export async function generateLocaleInterface(localesDir: string): Promise<void> { + const locale = yaml.load(fs.readFileSync(`${localesDir}/ja-JP.yml`, 'utf-8').toString()) as LocaleRecord; + const members = createMembers(locale); + + const elements: ts.Statement[] = [ + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + false, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier( + true, + undefined, + ts.factory.createIdentifier('ILocale'), + ), + ts.factory.createImportSpecifier( + true, + undefined, + ts.factory.createIdentifier('ParameterizedString'), + ), + ]), + ), + ts.factory.createStringLiteral('../types.js'), + undefined, + ), + ts.factory.createInterfaceDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier('Locale'), + undefined, + [ + ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ + ts.factory.createExpressionWithTypeArguments( + ts.factory.createIdentifier('ILocale'), + undefined, + ), + ]), + ], + members, + ), + ]; + + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.MultiLineCommentTrivia, + ' eslint-disable ', + true, + ); + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ' This file is generated by scripts/generateLocaleInterface.ts', + true, + ); + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ' Do not edit this file directly.', + true, + ); + + const printed = ts + .createPrinter({ + newLine: ts.NewLineKind.LineFeed, + }) + .printList( + ts.ListFormat.MultiLine, + ts.factory.createNodeArray(elements), + ts.createSourceFile( + 'locale.ts', + '', + ts.ScriptTarget.ESNext, + true, + ts.ScriptKind.TS, + ), + ); + + const autogenDir = `${__dirname}/../src/autogen`; + fs.mkdirSync(autogenDir, { recursive: true }); + + // 一瞬ファイルが存在しなくなって途切れる→不安定になるらしいので、リネームで対処 + fs.writeFileSync(`${autogenDir}/_locale.ts`, printed, 'utf-8'); + fs.renameSync(`${autogenDir}/_locale.ts`, `${autogenDir}/locale.ts`); +} + +// スクリプトとして直接実行された場合 +const isMain = import.meta.url === `file://${process.argv[1]}`; +if (isMain) { + await generateLocaleInterface(resolve(__dirname, '../../../locales')); +} |