(add) i18n members credit

This commit is contained in:
kakkokari-gtyih 2023-12-24 14:47:17 +09:00
parent 783990a1df
commit cee4e6b6e7
8 changed files with 148 additions and 7 deletions

3
.gitignore vendored
View File

@ -25,3 +25,6 @@ logs
# Locale build outputs # Locale build outputs
locales_dist locales_dist
# Crowdin Members Result
assets/data/i18n-members.ts

View File

@ -20,6 +20,11 @@ export type MiHubMember = {
type: 'website'; type: 'website';
href: string; href: string;
})[]; })[];
} | {
id: 'crowdin';
username: string;
name?: string;
avatar?: string;
}; };
/** 現行のコアチームメンバー */ /** 現行のコアチームメンバー */

View File

@ -1,7 +1,7 @@
<template> <template>
<GNuxtLink :to="`https://github.com/${member.username}`" target="_blank" class="block p-4 bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 rounded-lg"> <GNuxtLink :to="member.id === 'crowdin' ? `https://crowdin.com/profile/${member.username}` : `https://github.com/${member.username}`" target="_blank" class="flex items-center p-4 bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 rounded-lg">
<div class="flex space-x-4 items-center"> <div class="flex space-x-4 items-center">
<img :src="`https://avatars.githubusercontent.com/u/${member.id}?s=80&v=4`" class="block aspect-square flex-shrink-0 h-10 rounded-full" loading="lazy" /> <img :src="member.id === 'crowdin' ? member.avatar : `https://avatars.githubusercontent.com/u/${member.id}?s=80&v=4`" class="block aspect-square flex-shrink-0 h-10 rounded-full" loading="lazy" />
<div> <div>
<div class="text-lg">{{ member.name ?? '@' + member.username }}</div> <div class="text-lg">{{ member.name ?? '@' + member.username }}</div>
<div v-if="member.name" class="text-sm opacity-80">{{ '@' + member.username }}</div> <div v-if="member.name" class="text-sm opacity-80">{{ '@' + member.username }}</div>

View File

@ -248,6 +248,12 @@ _aboutUs:
_orgPartner: _orgPartner:
title: "企業・団体パートナー" title: "企業・団体パートナー"
description: "こちらの企業・団体から、金銭的・技術的な観点でMisskeyの開発へご協力いただいております。" description: "こちらの企業・団体から、金銭的・技術的な観点でMisskeyの開発へご協力いただいております。"
_hubTranslators:
title: "Misskey Hub ローカライザー"
description: "Misskey Hubの翻訳に協力いただいている皆さんです。"
_coreTranslators:
title: "Misskey ローカライザー"
description: "Misskeyの翻訳に協力いただいている皆さんです。"
_i18n: _i18n:
_missing: _missing:

View File

@ -8,6 +8,7 @@ import { genLocalesJson } from './scripts/gen-locales';
import { getStaticEndpoints } from './scripts/get-static-endpoints'; import { getStaticEndpoints } from './scripts/get-static-endpoints';
import { locales } from './assets/data/locales'; import { locales } from './assets/data/locales';
import type { NuxtConfig } from 'nuxt/schema'; import type { NuxtConfig } from 'nuxt/schema';
import { fetchCrowdinMembers } from './scripts/fetch-crowdin';
// 公開時のドメイン(末尾スラッシュなし) // 公開時のドメイン(末尾スラッシュなし)
const baseUrl = const baseUrl =
@ -64,7 +65,8 @@ export default defineNuxtConfig({
baseUrl, baseUrl,
repositoryUrl, repositoryUrl,
locales, locales,
} },
CROWDIN_INTG_API: process.env.CROWDIN_INTG_API,
}, },
css: [ css: [
"github-markdown-css/github-markdown.css", "github-markdown-css/github-markdown.css",
@ -170,9 +172,10 @@ export default defineNuxtConfig({
], ],
}, },
hooks: { hooks: {
'build:before': (...args) => { 'build:before': async (...args) => {
genApiTranslationFiles(...args); genApiTranslationFiles(...args);
genLocalesJson(...args); genLocalesJson(...args);
await fetchCrowdinMembers(...args);
}, },
}, },
experimental: { experimental: {

View File

@ -17,7 +17,25 @@
</GHero> </GHero>
<div class="pb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950"> <div class="pb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="container mx-auto max-w-screen-xl px-6 pt-4 space-y-8"> <div class="container mx-auto max-w-screen-xl px-6 pt-4 space-y-8">
<div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot"> <GLocalNav :items="[
{
name: $t('_aboutUs._team._core.title'),
anchor: '#coreContributors',
},
{
name: $t('_aboutUs._team._coreEmeriti.title'),
anchor: '#coreEmeriti',
},
{
name: $t('_aboutUs._team._contributors.title'),
anchor: '#contributors',
},
{
name: $t('_aboutUs._team._hubTranslators.title'),
anchor: '#hubTranslators',
},
]" />
<div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot" id="coreContributors">
<div> <div>
<div class="lg:sticky lg:top-32"> <div class="lg:sticky lg:top-32">
<h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._core.title') }}</h3> <h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._core.title') }}</h3>
@ -30,7 +48,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot"> <div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot" id="coreEmeriti">
<div> <div>
<div class="lg:sticky lg:top-32"> <div class="lg:sticky lg:top-32">
<h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._coreEmeriti.title') }}</h3> <h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._coreEmeriti.title') }}</h3>
@ -43,7 +61,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot"> <div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot" id="contributors">
<div> <div>
<div class="lg:sticky lg:top-32"> <div class="lg:sticky lg:top-32">
<h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._contributors.title') }}</h3> <h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._contributors.title') }}</h3>
@ -62,6 +80,22 @@
</div> </div>
</div> </div>
</div> </div>
<div class="grid gap-x-12 gap-y-4 w-full" :class="$style.teamRoot" id="hubTranslators" v-if="hubI18nMembers">
<div>
<div class="lg:sticky lg:top-32">
<h3 class="font-title font-bold text-2xl lg:text-2xl mb-2 lg:mb-4">{{ $t('_aboutUs._team._hubTranslators.title') }}</h3>
<p>{{ $t('_aboutUs._team._hubTranslators.description') }}</p>
</div>
</div>
<div>
<div class="grid md:grid-cols-2 gap-4">
<template v-for="members, lang in hubI18nMembers">
<h4 class="font-bold text-xl _i18n pt-4 first:pt-0 md:col-span-2" :lang="locales.find((v) => v.code === lang)?.iso">{{ locales.find((v) => v.code === lang)?.name ?? lang }}</h4>
<AboutUsTeamMember v-for="member in members" :member="member" />
</template>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -69,7 +103,9 @@
<script setup lang="ts"> <script setup lang="ts">
import ExtIco from 'bi/box-arrow-up-right.svg'; import ExtIco from 'bi/box-arrow-up-right.svg';
import { locales } from '@/assets/data/locales';
import { coreTeamMember, coreTeamEmeriti } from '@/assets/data/team-members'; import { coreTeamMember, coreTeamEmeriti } from '@/assets/data/team-members';
import { hubI18nMembers } from '@/assets/data/i18n-members';
import type { MiHubMember } from '@/assets/data/team-members'; import type { MiHubMember } from '@/assets/data/team-members';
const { t } = useI18n(); const { t } = useI18n();

85
scripts/fetch-crowdin.ts Normal file
View File

@ -0,0 +1,85 @@
import { withQuery } from "ufo";
import { localesConst, type LocaleCodes } from "../assets/data/locales";
import type { PartialRecord } from "../types/others";
import { writeFileSync } from "fs";
import path from "path";
type CrowdinTargetLanguages = {
id: string;
name: string;
editorCode: string;
twoLettersCode: string;
threeLettersCode: string;
locale: string;
androidCode: string;
osxCode: string;
osxLocale: string;
pluralCategoryNames: string[];
pluralRules: string;
pluralExamples: string[];
textDirection: string;
dialectOf: string;
};
type CrowdinProjectMember = {
id: number;
username: string;
fullName: string;
role: string;
permissions: Record<string, string>;
roles: { name: string; permissions: Record<string, string> };
avatarUrl: string;
joinedAt: string;
timezone: string;
};
//@ts-ignore
export async function fetchCrowdinMembers() {
if (!process.env.CROWDIN_INTG_API) {
return;
}
const res = await fetch('https://api.crowdin.com/api/v2/projects/628502', {
headers: {
'Authorization': `Bearer ${process.env.CROWDIN_INTG_API}`,
},
});
const projectRes = await res.json();
const out: PartialRecord<LocaleCodes, any[]> = {};
for (let i = 0; i < projectRes.data.targetLanguages.length; i++) {
const lang = projectRes.data.targetLanguages[i] as CrowdinTargetLanguages;
const correspondLocaleObject = localesConst.find((v) => v.iso === lang.locale);
if (correspondLocaleObject !== undefined) {
const res = await fetch(withQuery('https://api.crowdin.com/api/v2/projects/628502/members', {
role: 'translator',
languageId: lang.id,
limit: 24,
}), {
headers: {
'Authorization': `Bearer ${process.env.CROWDIN_INTG_API}`,
},
});
const membersRes = await res.json();
out[correspondLocaleObject.code] = (membersRes.data as { data: CrowdinProjectMember }[]).map((v) => ({
id: 'crowdin',
username: v.data.username,
name: v.data.fullName ? v.data.fullName : undefined,
avatar: v.data.avatarUrl ? v.data.avatarUrl : undefined,
}));
}
}
const sourceFilePath = path.resolve(__dirname, '../assets/data/i18n-members.ts');
const tsOut = [
'/** This file is auto-generated */',
'import type { LocaleCodes } from \'@/assets/data/locales\';',
'import type { PartialRecord } from \'@/types/others\';',
'import type { MiHubMember } from \'./team-members\';',
];
tsOut.push(`export const hubI18nMembers: PartialRecord<LocaleCodes, MiHubMember[]> = ${JSON.stringify(out)};`);
writeFileSync(sourceFilePath, tsOut.join('\n'));
console.log('Crowdin (Misskey Hub) 貢献者の取得完了');
}

3
types/others.ts Normal file
View File

@ -0,0 +1,3 @@
export type PartialRecord<K extends keyof any, T> = {
[P in K]?: T;
};