多言語対応したサイトを作ってみます!
umi Camera | vintage film camera
https://umi.grtlab.com/
フィルムカメラ調の撮影カメラ、iPhone無料アプリです!
アプリ側では15言語対応しているので seo的にも 紹介サイトを多言語化しておこうかなと
今は日本語と英語だけなのですが
プロジェクト作成 umi-astro
% npm create astro@latest umi-astro
Need to install the following packages:
create-astro@4.13.2
Ok to proceed? (y) y
tmpl How would you like to start your new project?
A basic, helpful starter project
deps Install dependencies?
Yes
git Initialize a new git repository?
Yes
✔ Project initialized!
■ Template copied
■ Dependencies installed
■ Git initialized
next Liftoff confirmed. Explore your project!umi-astroディレクトリに作ります
npm installして起動確認
# 1. プロジェクトディレクトリに移動 (もし、まだ移動していなければ) cd umi-astro # 2. 依存関係をインストールする # package.json に基づいて、すべての必要なモジュールをダウンロードします。 npm install # 3. インストールが完了したら、開発サーバーを起動 npm run dev
run devで
% npm run dev > umi-astro@0.0.1 dev > astro dev 23:44:58 [types] Generated 1ms 23:44:58 [content] Syncing content 23:44:58 [content] Synced content astro v5.15.1 ready in 363 ms ┃ Local http://localhost:4321/ ┃ Network use --host to expose 23:44:58 watching for file changes... 23:45:08 [200] / 16ms
http://localhost:4321/にアクセスして確認OK
astro@michel5.15.1 versionは 5.15.1ですね
検索やaiで調べるときはversion指定しないと古い情報も多いです
Tailwind追加
cd umi-astroで tailwind 追加
% npx astro add tailwind
Ne % npx astro add tailwind
✔ Resolving packages...
Astro will run the following command:
If you skip this step, you can always run it yourself later
╭──────────────────────────────────────────────────────╮
│ npm i @tailwindcss/vite@^4.1.16 tailwindcss@^4.1.16 │
╰──────────────────────────────────────────────────────╯
✔ Continue? … yes
⠙ Installing dependencies...
Astro will scaffold ./src/styles/global.css.
✔ Continue? … yes
Astro will make the following changes to your config file:
╭ astro.config.mjs ─────────────────────────────╮
│ // @ts-check │
│ import { defineConfig } from 'astro/config'; │
│ │
│ import tailwindcss from '@tailwindcss/vite'; │
│ │
│ // https://astro.build/config │
│ export default defineConfig({ │
│ vite: { │
│ plugins: [tailwindcss()] │
│ } │
│ }); │
╰───────────────────────────────────────────────╯
✔ Continue? … yes
success Added the following integration to your project:
- tailwind
action required You must import your Tailwind stylesheet, e.g. in a shared layout:
╭ src/layouts/Layout.astro ──────╮
│ --- │
│ import '../styles/global.css' │
│ --- │
╰────────────────────────────────╯
とりあえずインストールOK
Astro i18n 多言語構成
astro.config.mjsの編集
// @ts-check
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
// ------------------------------
i18n: {
// ...i18n の設定
defaultLocale: 'en',
locales: ['en', 'ja'],
routing: {
prefixDefaultLocale: false,
},
},
vite: {
plugins: [tailwindcss()],
},
}); tailwindはvite経由で◎
翻訳ファイルの作成とルーティング
翻訳ディレクトリーとファイル作成
mkdir src/i18n touch src/i18n/ui.ts # すべての翻訳を統合するファイル
common.js
export const common_translations = {
en: {
'nav.home': 'Home',
'nav.about': 'About',
'language.en': 'English',
'language.ja': 'Japanese',
'footer.note': 'All rights reserved.',
},
ja: {
'nav.home': 'ホーム',
'nav.about': '会社概要',
'language.en': '英語',
'language.ja': '日本語',
'footer.note': '無断転載を禁じます。',
},
};home
export const home_translations = {
en: {
'page.title': 'Welcome to Our International Site',
'page.description': 'This is the English version.',
},
ja: {
'page.title': '国際サイトへようこそ',
'page.description': 'こちらは日本語版です。',
},
};ui.ts ( 統合 , ページごと
// src/i18n/ui.ts
import type { Translations } from './dictionary'; // ⭐️ 新しく作成した型をインポート
/**
* デフォルト言語を定義します。
* 'en' がデフォルト言語(プレフィックスなしの URL 例: /about)
*/
export const defaultLang = 'en';
/**
* 翻訳辞書。
* すべての翻訳キーとその値を言語ごとに定義します。
*/
// ui オブジェクトに Translations 型を適用
export const ui: Translations = {
en: {
// --- アプリ共通情報 ---
'app.name': 'umi Retro Snap',
'app.tagline': 'film look camera with live filters – no editing required',
'app.download': 'Download on the App Store',
'country.japan': 'Japan',
},
ja : {
},
};// src/i18n/utils.ts
import { ui, defaultLang } from './ui';
import type { AvailableKeys } from './dictionary'; // ⭐️ 新しくインポート
// 1. 翻訳関数 (t) の生成
export function getI18nTranslator(locale: keyof typeof ui) {
const targetLocale = ui.hasOwnProperty(locale) ? locale : defaultLang;
// 翻訳関数 t は AvailableKeys 型のキーのみを受け付ける
return function t(key: AvailableKeys) {
// ⬇️ この行でエラーが発生していました
// ロケールが見つからない場合、あるいはキーが存在しない場合に備えて、
// ターゲットロケールの翻訳、デフォルトロケールの翻訳、最後にキー名自体を返す
// ui[targetLocale] が未定義になる可能性は排除済み
const translation = ui[targetLocale]?.[key] || ui[defaultLang]?.[key];
// どの辞書にもキーが存在しない場合 (本来は型で防ぐべきだが、安全策として)
return translation || key;
}
}
// 2. 言語切り替えURLの生成 (変更なし)
export function getLocaleUrl(targetLocale: keyof typeof ui, currentPath: string): string {
// ... 以前のロジック ...
const pathWithoutLocale = currentPath.replace(/^\/(ja|en)/, '');
if (targetLocale === defaultLang) {
return pathWithoutLocale || '/';
} else {
return `/${targetLocale}${pathWithoutLocale === '/' ? '' : pathWithoutLocale}`;
}
}なんか辞書でエラーになるのでこんな感じに。。。
言語スイッチャーコンポーネントの作成
src/components/LanguageSwitcher.astro
---
// ✅ カスタムユーティリティのみをインポート
import { getI18nTranslator, getLocaleUrl } from '../i18n/utils';
import { ui } from '../i18n/ui';
// ✅ ロケールは 'Astro.currentLocale' を使用 (これは安定しているはず)
const locale = Astro.currentLocale as keyof typeof ui;
const locales = ['en', 'ja'] as const;
const t = getI18nTranslator(locale);
// URL生成のための現在のパスを取得
const currentPath = Astro.url.pathname;
---
<div class="flex space-x-2">
{locales.map((lang) => (
<a
// ✅ カスタムヘルパーで URL を生成
href={getLocaleUrl(lang, currentPath)}
class={`px-3 py-1 rounded-full text-sm transition-colors ${
locale === lang
? 'bg-white text-blue-600 font-bold'
: 'bg-blue-500 hover:bg-blue-400 text-white'
}`}
>
{t(`language.${lang}`)}
</a>
))}
</div>ダイナミックルーティング
/ /ja/ /fr/ みたいに切り替える方法です
| 記法 | 機能名 | 用途 |
[...lang] | Rest パラメータを持つダイナミックルーティング | 任意の深さのパスセグメントを、単一のパラメータとしてキャッチします。多言語(i18n)やブログのカスタムURLなどに使用されます。 |
[slug] | ダイナミックルーティング | 単一のパスセグメント(例: /posts/hello-world の hello-world)をパラメータとしてキャッチします。 |
ほー。
Rest パラメータとは?
残余引数 !!!!なにそれ^^;
「残りのパス(URLセグメント)をまとめて捕捉するパラメータ」実働はわかるのですが、言葉での説明は難しいですね
今回は […lang]で lang配列を処理できるのですが
slugもuriの /slug/slug/ というのはわかりますが日本語でなんなの?と
uriにつかうキーワード識別子という感じですがわかってはいても知らない人への説明は難しいものですね
src/pages/[…lang]/index.astro
---
// ===============================================
// 1. Astro API と ユーティリティのインポート
// ===============================================
import Layout from '../../layouts/Layout.astro'; // 相対パスを修正
import LanguageSwitcher from '../../components/LanguageSwitcher.astro';
import { getI18nTranslator } from '../../i18n/utils';
import { ui } from '../../i18n/ui';
// ===============================================
// 2. ダイナミックルーティングとロケールの取得
// ===============================================
export function getStaticPaths() {
const locales = ['en', 'ja'];
return locales.map(lang => ({
params: { lang: lang === 'en' ? undefined : lang }, // デフォルト言語 'en' はプレフィックスなし
props: { locale: lang },
}));
}
// Props から locale を受け取る
const { locale } = Astro.props;
const t = getI18nTranslator(locale);<Layout title={t('page.title')} locale={locale} showHero={true}>
<main class="text-gray-800">
<section id="about" class="py-16 max-w-4xl mx-auto px-4">
<h2 class="text-3xl font-bold text-gray-900 mb-8 border-b pb-2">
{t('section.about.title')}
</h2>
<div class="grid md:grid-cols-2 gap-12 items-center">
<div class="bg-gray-200 h-80 rounded-xl flex items-center justify-center">
</div>
<div>
<p class="text-lg mb-6 leading-relaxed">
{t('section.about.p1')}
</p>
<ul class="space-y-4">
<li class="flex items-start">
<span class="text-blue-500 mr-3 text-xl">✅ </span>
<div>
<h3 class="font-semibold text-lg">{t('feature.1.title')}</h3>
<p class="text-gray-600 text-sm">{t('feature.1.desc')}</p>
</div>
</li>
<li class="flex items-start">
<span class="text-blue-500 mr-3 text-xl">�️ </span>
<div>
<h3 class="font-semibold text-lg">{t('feature.2.title')}</h3>
<p class="text-gray-600 text-sm">{t('feature.2.desc')}</p>
</div>
</li>
</ul>
</div>
</div>
</section>
</main>
</Layout>{t(‘abc.def’)} が ui.tsに入っているパラメータです
これでテンプレート ( index.astro ) を1ページ分作ると あとは翻訳を入れるだけで すべての翻訳ページが生成できます! OK^^
最初の astro使用記事はこちら

Astro –
https://astro.build/
TailWind CSS –
https://tailwindcss.com/


コメント