summaryrefslogtreecommitdiff
path: root/packages/i18n/scripts/generateLocaleInterface.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/i18n/scripts/generateLocaleInterface.ts')
-rw-r--r--packages/i18n/scripts/generateLocaleInterface.ts153
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'));
+}