mirror of
https://github.com/vrc-get/vrc-get.git
synced 2026-06-21 09:58:08 +00:00
261 lines
7.9 KiB
JavaScript
261 lines
7.9 KiB
JavaScript
const fs = require('node:fs/promises');
|
|
const json5 = require('json5');
|
|
|
|
// Those keys are optional; they are not required to be translated and might be translated only by some locales.
|
|
const optionalKeys = [
|
|
"projects:manage:dialog:downgrade major vrchat supported",
|
|
"projects:manage:dialog:downgrade major vrchat unsupported",
|
|
"projects:manage:dialog:downgrade minor vrchat supported",
|
|
"projects:manage:dialog:downgrade minor vrchat unsupported",
|
|
"projects:manage:dialog:upgrade minor vrchat supported",
|
|
"projects:manage:dialog:upgrade minor vrchat unsupported",
|
|
"projects:manage:dialog:upgrade major vrchat supported",
|
|
"projects:manage:dialog:upgrade major vrchat unsupported",
|
|
]
|
|
|
|
/**
|
|
* @param github {import('@octokit/rest').Octokit}
|
|
* @param context {{repo: {owner: string, repo: string}}}
|
|
*/
|
|
module.exports = async ({github, context}) => {
|
|
if (context.repo.owner !== 'vrc-get') return;
|
|
|
|
const {owner, repo} = context.repo;
|
|
|
|
const locales = [
|
|
{
|
|
id: 'ja',
|
|
discussionNumber: 855,
|
|
replyId: 'DC_kwDOIza9ks4AjSve',
|
|
},
|
|
{
|
|
id: 'de',
|
|
discussionNumber: 860,
|
|
replyId: 'DC_kwDOIza9ks4AjS5I',
|
|
},
|
|
{
|
|
id: 'zh_hans',
|
|
discussionNumber: 888,
|
|
replyId: 'DC_kwDOIza9ks4AjUwo',
|
|
},
|
|
{
|
|
id: 'fr',
|
|
discussionNumber: 909,
|
|
replyId: 'DC_kwDOIza9ks4Aji4V',
|
|
},
|
|
{
|
|
id: 'zh_hant',
|
|
discussionNumber: 1443,
|
|
replyId: 'DC_kwDOIza9ks4An6A8'
|
|
},
|
|
{
|
|
id: 'ko',
|
|
discussionNumber: 1823,
|
|
replyId: 'DC_kwDOIza9ks4AswKE'
|
|
},
|
|
];
|
|
|
|
/** @type {{missingCount: number, extraCount: number, id: string, discussionNumber: number}[]} */
|
|
const localeData = [];
|
|
|
|
for (const locale of locales) {
|
|
const proceed = await processOneLocale(github, owner, repo, locale.discussionNumber, locale.replyId, locale.id);
|
|
localeData.push({
|
|
id: locale.id,
|
|
discussionNumber: locale.discussionNumber,
|
|
missingCount: proceed.missingCount,
|
|
extraCount: proceed.extraCount,
|
|
})
|
|
}
|
|
|
|
// 894 is English Localization and Text Representation
|
|
await updateRootLocale(github, owner, repo, 894, localeData);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param github {import('@octokit/rest').Octokit}
|
|
* @param owner {string}
|
|
* @param repo {string}
|
|
* @param number {number}
|
|
* @param replyToId {string}
|
|
* @param localeId {string}
|
|
* @return {Promise<{missingCount: number, extraCount: number}>}
|
|
*/
|
|
async function processOneLocale(github, owner, repo, number, replyToId, localeId) {
|
|
const enJson = json5.parse(await fs.readFile(`vrc-get-gui/locales/en.json5`, "utf8"));
|
|
const enKeys = normalizeKeys(Object.keys(enJson.translation));
|
|
const transJson = json5.parse(await fs.readFile(`vrc-get-gui/locales/${localeId}.json5`, "utf8"));
|
|
const transKeys = normalizeKeys(Object.keys(transJson.translation));
|
|
|
|
const {missingList: missingKeys, extraList: extraKeys} = missingAndExtras(enKeys, transKeys);
|
|
|
|
const newData = {
|
|
missingKeys,
|
|
extraKeys,
|
|
};
|
|
|
|
const newAutoPart = `**Missing Keys:**\n${listToMarkdown(missingKeys)}\n\n**Excess Keys:**\n${listToMarkdown(extraKeys)}\n`;
|
|
|
|
const {discussionId, previousJson: dataJson} = await updateComment(github, owner, repo, number, newAutoPart, newData);
|
|
dataJson.missingKeys ??= [];
|
|
dataJson.extraKeys ??= [];
|
|
|
|
// create comment if there are new missing / extra keys
|
|
const {extraList: newlyAddedMissingKeys} = missingAndExtras(normalizeKeys(dataJson.missingKeys), missingKeys);
|
|
const {extraList: newlyAddedExtraKeys} = missingAndExtras(normalizeKeys(dataJson.extraKeys), extraKeys);
|
|
if (newlyAddedMissingKeys.length > 0 || newlyAddedExtraKeys.length > 0) {
|
|
const text = `
|
|
There are new missing / excess keys in the translation. Please update the translation!
|
|
|
|
**New Missing Keys:**
|
|
|
|
${listToMarkdown(newlyAddedMissingKeys)}
|
|
|
|
**New Excess Keys:**
|
|
|
|
${listToMarkdown(newlyAddedExtraKeys)}
|
|
`
|
|
|
|
await github.graphql(`
|
|
mutation($discussionId: ID!, $replyToId: ID!, $body: String!) {
|
|
addDiscussionComment(input: {discussionId: $discussionId, replyToId: $replyToId, body: $body}) {
|
|
comment {
|
|
body
|
|
}
|
|
}
|
|
}
|
|
`, {discussionId, replyToId, body: text});
|
|
}
|
|
|
|
return {
|
|
missingCount: missingKeys.length,
|
|
extraCount: extraKeys.length,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the root locale configuration for a specified repository.
|
|
*
|
|
* @param {import('@octokit/rest').Octokit} github - The GitHub API client instance used to interact with the GitHub API.
|
|
* @param {string} owner - The owner of the repository where the root locale is to be updated.
|
|
* @param {string} repo - The name of the repository where the root locale is to be updated.
|
|
* @param {number} number
|
|
* @param {{missingCount: number, extraCount: number, id: string, discussionNumber: number}[]} localeData - The locale data object containing the updated root locale configuration.
|
|
* @return {Promise<void>} A promise that resolves to the API response for the update operation.
|
|
*/
|
|
async function updateRootLocale(github, owner, repo, number, localeData) {
|
|
let table = "| locale | missing count | exceeding count | link |\n" +
|
|
"| -- | -- | -- | -- |\n"
|
|
for (let {missingCount, extraCount, id, discussionNumber} of localeData) {
|
|
table += `| ${id} | ${missingCount} | ${extraCount} | [link](https://github.com/${owner}/${repo}/discussions/${discussionNumber}) |\n`;
|
|
}
|
|
|
|
await updateComment(github, owner, repo, number, table, {});
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param beforeList {T[]}
|
|
* @param afterList {T[]}
|
|
* @return {{missingList: T[], extraList: T[]}}
|
|
*/
|
|
function missingAndExtras(beforeList, afterList) {
|
|
const missingList = beforeList.filter(key => !afterList.includes(key)).filter(key => !optionalKeys.includes(key));
|
|
const extraList = afterList.filter(key => !beforeList.includes(key)).filter(key => !optionalKeys.includes(key));
|
|
|
|
return {missingList, extraList};
|
|
}
|
|
|
|
function listToMarkdown(values) {
|
|
return values.length === 0 ? 'nothing' : values.map(key => `- \`${key}\``).join('\n')
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param github {import('@octokit/rest').Octokit}
|
|
* @param owner {string}
|
|
* @param repo {string}
|
|
* @param number {number}
|
|
* @param content {string} the updated content
|
|
* @param newData {object} data stored in the comment
|
|
* @return {Promise<{previousJson: object, discussionId:string}>}
|
|
*/
|
|
async function updateComment(
|
|
github,
|
|
owner, repo, number,
|
|
content,
|
|
newData,
|
|
) {
|
|
/** @type {{data: {repository: {discussion: {body: string}}}}} */
|
|
const result = await github.graphql(`
|
|
query($owner: String!, $repo: String!, $number: Int!) {
|
|
repository(owner: $owner, name: $repo) {
|
|
discussion(number: $number) {
|
|
body
|
|
id
|
|
}
|
|
}
|
|
}
|
|
`, {owner, repo, number})
|
|
|
|
const body = result.repository.discussion.body;
|
|
const discussionId = result.repository.discussion.id;
|
|
|
|
const dataJsonLinePrefix = "prevData: ";
|
|
const autoPartStart = "<!-- github actions update start -->";
|
|
const autoPartEnd = "<!-- github actions update end -->";
|
|
|
|
const split = body.split(autoPartStart, 2);
|
|
const manualPart = split[0];
|
|
const temp = split[1] ?? '';
|
|
const split1 = temp.split(autoPartEnd, 2);
|
|
const autoPart = split1[0];
|
|
const postAutoPart = split1[1] ?? '';
|
|
|
|
const dataJsonLine = autoPart.split(/\r?\n/).find(l => l.startsWith(dataJsonLinePrefix));
|
|
const previousJson = dataJsonLine ? JSON.parse(dataJsonLine.slice(dataJsonLinePrefix.length)) : {};
|
|
|
|
const newBody = `${manualPart}${autoPartStart}
|
|
${content}
|
|
<!-- data part
|
|
${dataJsonLinePrefix}${JSON.stringify(newData)}
|
|
-->
|
|
${autoPartEnd}${postAutoPart}`;
|
|
|
|
await github.graphql(`
|
|
mutation($discussionId: ID!, $body: String!) {
|
|
updateDiscussion(input: {discussionId: $discussionId, body: $body}) {
|
|
discussion {
|
|
body
|
|
}
|
|
}
|
|
}
|
|
`, {discussionId, body: newBody});
|
|
|
|
return {
|
|
previousJson,
|
|
discussionId,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param keys {string[]}
|
|
* @return {string[]}
|
|
*/
|
|
function normalizeKeys(keys) {
|
|
return keys.map(k => k.replace(/_(one|other)/, ''));
|
|
}
|
|
|
|
if (require.main === module) {
|
|
const {Octokit} = require('@octokit/rest');
|
|
module.exports({
|
|
github: new Octokit({auth: process.env.GITHUB_TOKEN}),
|
|
context: {
|
|
repo: {
|
|
owner: process.env.REPO_OWNER,
|
|
repo: process.env.REPO_NAME,
|
|
},
|
|
},
|
|
});
|
|
}
|