Skip to content

Commit 435e1ac

Browse files
enhance(i18n): i18nRedirectorを改良
1 parent 5aea98f commit 435e1ac

File tree

3 files changed

+59
-41
lines changed

3 files changed

+59
-41
lines changed

.vitepress/config/index.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import { defineConfig } from 'vitepress';
22
import type { LocaleConfig, DefaultTheme, HeadConfig } from 'vitepress';
3+
34
import { shared } from './shared';
45
import { ja } from './ja';
56
import { en } from './en';
67

7-
const locales: LocaleConfig<DefaultTheme.Config> = {
8+
import { genI18nRedirector } from '../scripts/gen-i18n-redirector';
9+
10+
const locales = {
811
ja: { label: '日本語', ...ja },
912
en: { label: 'English', ...en },
10-
};
13+
} as const satisfies LocaleConfig<DefaultTheme.Config>;
14+
15+
export const mainLocale = 'ja' as const satisfies keyof typeof locales;
1116

12-
const baseUrl = 'https://aiscript-dev.github.io';
17+
export const baseUrl = 'https://aiscript-dev.github.io';
1318

1419
export default defineConfig({
1520
...shared,
@@ -44,4 +49,5 @@ export default defineConfig({
4449

4550
return head;
4651
},
52+
buildEnd: genI18nRedirector,
4753
});
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//@ts-expect-error Node
2+
import fs from 'fs';
3+
import { mainLocale, baseUrl } from '../config';
4+
import type { SiteConfig } from 'vitepress';
5+
6+
async function createFile(path: string, content: string) {
7+
const dir = path.replace(/\/[^/]+$/, '');
8+
await fs.promises.writeFile(path, content).catch((err) => {
9+
if (err.code === 'ENOENT') {
10+
fs.promises.mkdir(dir, { recursive: true }).then(() => createFile(path, content));
11+
}
12+
});
13+
}
14+
15+
export async function genI18nRedirector(siteConfig: SiteConfig) {
16+
const routes = siteConfig.pages
17+
.filter((page) => page.startsWith(`${mainLocale}/`))
18+
.map((page) => page.replace(new RegExp(`^${mainLocale}\/`), '').replace(/\.md$/, '.html'));
19+
20+
const promises = routes.map((route) => {
21+
const localeNames = Object.keys(siteConfig.site.locales);
22+
const routeForRender = route.replace(/index\.html$/, '');
23+
const linkAlternate = localeNames.map((name) => `<link rel="alternate" hreflang="${siteConfig.site.locales[name].lang || name}" href="${baseUrl}/${name}/${routeForRender}">`).join('\n ');
24+
const fallbackLinks = localeNames.map((name) => `<a href="${baseUrl}/${name}/${routeForRender}">${siteConfig.site.locales[name].label}</a>`).join(', ');
25+
const content = `<!DOCTYPE html>
26+
<html lang="${siteConfig.site.locales[mainLocale].lang || 'ja-JP'}">
27+
<head>
28+
<meta charset="UTF-8">
29+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
30+
<title>Redirecting...</title>
31+
${linkAlternate}
32+
<link rel="alternate" hreflang="x-default" href="${baseUrl}/${mainLocale}/${routeForRender}">
33+
<link rel="canonical" href="${baseUrl}/${mainLocale}/${routeForRender}">
34+
<script type="text/javascript">const s = ${JSON.stringify(localeNames)}; const d = localStorage.getItem('ais:locale'); if (d) { location.replace('/' + d + location.pathname + location.search); } else if (s.includes(navigator.language.split("-")[0])) { location.replace('/' + navigator.language.split("-")[0] + location.pathname + location.search); } else { location.replace('/ja' + location.pathname + location.search); }</script>
35+
</head>
36+
<body>
37+
<noscript>${fallbackLinks}</noscript>
38+
</body>
39+
</html>
40+
`;
41+
return createFile(`${siteConfig.outDir}/${route}`, content);
42+
});
43+
44+
await Promise.allSettled(promises);
45+
46+
console.log('I18n redirector generated');
47+
}

.vitepress/theme/Layout.vue

+3-38
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,17 @@
11
<template>
2-
<div>
3-
<DefaultTheme.Layout />
4-
<div v-if="!loaded" :class="$style.w"></div>
5-
</div>
2+
<DefaultTheme.Layout />
63
</template>
74

85
<script setup lang="ts">
9-
import { watch, ref } from 'vue';
10-
import { useData, useRoute, inBrowser } from 'vitepress';
6+
import { watch } from 'vue';
7+
import { useData, inBrowser } from 'vitepress';
118
import DefaultTheme from 'vitepress/theme';
129
1310
const data = useData();
14-
const route = useRoute();
15-
const loaded = ref(false);
16-
17-
const locales = Object.keys(data.site.value.locales) as string[];
18-
const localesRegex = new RegExp(`^/(${locales.join('|')})`);
19-
const savedLocale = inBrowser ? localStorage.getItem('ais:locale') : null;
20-
21-
if (inBrowser) {
22-
if (!localesRegex.test(route.path)) {
23-
if (savedLocale != null && locales.includes(savedLocale)) {
24-
location.replace('/' + savedLocale + location.pathname + location.search);
25-
} else if (locales.includes(navigator.language.split('-')[0])) {
26-
location.replace('/' + navigator.language.split('-')[0] + location.pathname + location.search);
27-
} else {
28-
location.replace('/ja' + location.pathname + location.search);
29-
}
30-
} else {
31-
loaded.value = true;
32-
}
33-
}
3411
3512
watch(data.lang, (lang) => {
3613
if (inBrowser) {
3714
localStorage.setItem('ais:locale', lang.split('-')[0]);
3815
}
3916
}, { immediate: true });
4017
</script>
41-
42-
<style module>
43-
.w {
44-
position: fixed;
45-
top: 0;
46-
left: 0;
47-
width: 100vw;
48-
height: 100vh;
49-
background-color: var(--vp-c-bg);
50-
z-index: 9999;
51-
}
52-
</style>

0 commit comments

Comments
 (0)