UX全体にテーマと言語設定を追加。 - lib/db/migrations/004_user_prefs.sql: users に theme/locale 列を追加(既定 dark/en) - lib/i18n/: dictionary(en/ja) + I18nProvider(クライアントcontext: locale/theme/t/setLocale/setTheme) + server.ts(SSR用 getLocale/getTheme/translate) + constants.ts - app/layout.tsx: theme/locale Cookie を読み <html class/lang> をSSR、I18nProvider でラップ(フラッシュなし) - tailwind darkMode:'class' + 全画面に dark: バリアントを一括付与(Nodeスクリプト lookbehind で安全に変換) - components/layout/ThemeToggle: 即時クラス切替+Cookie+永続化 - app/profile: テーマ/言語セレクタ、chrome翻訳(Header/ProjectNav/login/dashboard/profile) - PATCH /api/users/me: theme/locale を受理(バリデーション→400)、Cookieを設定 - E2E: locale=ja storageState で既存JAアサーションを維持、theme-i18n.spec で既定en/darkと切替を検証
173 lines
5.9 KiB
TypeScript
173 lines
5.9 KiB
TypeScript
import type { Locale } from '@/lib/types';
|
|
|
|
/**
|
|
* 多言語辞書。en/ja の両方に同じキーを定義する。
|
|
* 共通クロム(ヘッダー/ナビ)、認証、プロフィール/設定、ページタイトル、
|
|
* 共通アクションラベルをカバーする。機能画面の本文は漸次追加する。
|
|
*/
|
|
const en = {
|
|
'app.name': 'Groupware',
|
|
'nav.dashboard': 'Dashboard',
|
|
'nav.overview': 'Overview',
|
|
'nav.board': 'Board',
|
|
'nav.notes': 'Notes',
|
|
'nav.chat': 'Chat',
|
|
'nav.todos': 'ToDo',
|
|
'nav.files': 'Files',
|
|
'nav.calendar': 'Calendar',
|
|
'nav.milestones': 'Milestones',
|
|
'nav.meetings': 'Meetings',
|
|
'nav.search': 'Search',
|
|
'nav.members': 'Members',
|
|
'nav.activity': 'Activity',
|
|
'nav.settings': 'Settings',
|
|
'header.logout': 'Logout',
|
|
'common.save': 'Save',
|
|
'common.cancel': 'Cancel',
|
|
'common.create': 'Create',
|
|
'common.delete': 'Delete',
|
|
'common.submit': 'Submit',
|
|
'common.loading': 'Loading...',
|
|
'common.back': 'Back',
|
|
'common.edit': 'Edit',
|
|
'theme.dark': 'Dark',
|
|
'theme.light': 'Light',
|
|
'language.english': 'English',
|
|
'language.japanese': '日本語',
|
|
'auth.login': 'Login',
|
|
'auth.register': 'Register',
|
|
'auth.displayName': 'Display name',
|
|
'auth.email': 'Email',
|
|
'auth.password': 'Password',
|
|
'auth.registerHere': 'New here? Register',
|
|
'auth.registerButton': 'Register',
|
|
'auth.loginButton': 'Login',
|
|
'auth.backToLogin': 'Back to login',
|
|
'auth.processing': 'Processing...',
|
|
'auth.failed': 'Operation failed',
|
|
'auth.projectName': 'Project name',
|
|
'auth.newProject': 'New project',
|
|
'project.description': 'Description (optional)',
|
|
'project.creating': 'Creating...',
|
|
'project.createFailed': 'Failed to create project',
|
|
'profile.title': 'Profile',
|
|
'profile.displayName': 'Display name',
|
|
'profile.email': 'Email',
|
|
'profile.avatarUrl': 'Avatar image URL',
|
|
'profile.saved': 'Profile updated',
|
|
'profile.backToDashboard': 'Back to dashboard',
|
|
'profile.role': 'Role',
|
|
'profile.theme': 'Theme',
|
|
'profile.language': 'Language',
|
|
'page.dashboard': 'Dashboard',
|
|
'page.board': 'Board',
|
|
'page.notes': 'Notes',
|
|
'page.chat': 'Chat',
|
|
'page.todos': 'ToDo / Kanban',
|
|
'page.files': 'Files',
|
|
'page.calendar': 'Calendar',
|
|
'page.milestones': 'Milestones',
|
|
'page.meetings': 'Meetings',
|
|
'page.search': 'Search',
|
|
'page.members': 'Members',
|
|
'page.activity': 'Activity',
|
|
'page.settings': 'Settings',
|
|
'page.profile': 'Profile',
|
|
'page.login': 'Login',
|
|
'dash.projects': 'Joined projects',
|
|
'dash.unreadNotifications': 'Unread notifications',
|
|
'dash.incompleteTodos': 'Incomplete ToDo',
|
|
'dash.overdueTasks': 'Overdue tasks',
|
|
'dash.upcomingMeetings': 'Upcoming meetings',
|
|
'dash.recentActivity': 'Recent activity',
|
|
'dash.empty': 'None',
|
|
'dash.noUnread': 'No unread',
|
|
'dash.openNotifications': 'Open notifications',
|
|
'dash.due': 'Due',
|
|
};
|
|
|
|
const ja: typeof en = {
|
|
'app.name': 'シンプルグループウェア',
|
|
'nav.dashboard': 'ダッシュボード',
|
|
'nav.overview': '概要',
|
|
'nav.board': '掲示板',
|
|
'nav.notes': 'メモ',
|
|
'nav.chat': 'チャット',
|
|
'nav.todos': 'ToDo',
|
|
'nav.files': 'ファイル',
|
|
'nav.calendar': 'カレンダー',
|
|
'nav.milestones': 'マイルストーン',
|
|
'nav.meetings': 'ミーティング',
|
|
'nav.search': '検索',
|
|
'nav.members': 'メンバー',
|
|
'nav.activity': 'アクティビティ',
|
|
'nav.settings': '設定',
|
|
'header.logout': 'ログアウト',
|
|
'common.save': '保存',
|
|
'common.cancel': 'キャンセル',
|
|
'common.create': '作成',
|
|
'common.delete': '削除',
|
|
'common.submit': '送信',
|
|
'common.loading': '読み込み中...',
|
|
'common.back': '戻る',
|
|
'common.edit': '編集',
|
|
'theme.dark': 'ダーク',
|
|
'theme.light': 'ライト',
|
|
'language.english': 'English',
|
|
'language.japanese': '日本語',
|
|
'auth.login': 'ログイン',
|
|
'auth.register': '新規登録',
|
|
'auth.displayName': '表示名',
|
|
'auth.email': 'メールアドレス',
|
|
'auth.password': 'パスワード',
|
|
'auth.registerHere': '新規登録はこちら',
|
|
'auth.registerButton': '登録する',
|
|
'auth.loginButton': 'ログイン',
|
|
'auth.backToLogin': 'ログイン画面に戻る',
|
|
'auth.processing': '処理中...',
|
|
'auth.failed': '処理に失敗しました',
|
|
'auth.projectName': 'プロジェクト名',
|
|
'auth.newProject': '新規プロジェクト',
|
|
'project.description': '説明(任意)',
|
|
'project.creating': '作成中...',
|
|
'project.createFailed': 'プロジェクトの作成に失敗しました',
|
|
'profile.title': 'プロフィール',
|
|
'profile.displayName': '表示名',
|
|
'profile.email': 'メールアドレス',
|
|
'profile.avatarUrl': 'アイコン画像URL',
|
|
'profile.saved': 'プロフィールを更新しました',
|
|
'profile.backToDashboard': 'ダッシュボードへ',
|
|
'profile.role': 'ロール',
|
|
'profile.theme': 'テーマ',
|
|
'profile.language': '言語',
|
|
'page.dashboard': 'ダッシュボード',
|
|
'page.board': '掲示板',
|
|
'page.notes': 'メモ',
|
|
'page.chat': 'チャット',
|
|
'page.todos': 'ToDo / Kanban',
|
|
'page.files': 'ファイル',
|
|
'page.calendar': 'カレンダー',
|
|
'page.milestones': 'マイルストーン',
|
|
'page.meetings': 'ミーティング',
|
|
'page.search': '検索',
|
|
'page.members': 'メンバー',
|
|
'page.activity': 'アクティビティ',
|
|
'page.settings': '設定',
|
|
'page.profile': 'プロフィール',
|
|
'page.login': 'ログイン',
|
|
'dash.projects': '参加プロジェクト',
|
|
'dash.unreadNotifications': '未読通知',
|
|
'dash.incompleteTodos': '未完了ToDo',
|
|
'dash.overdueTasks': '期限切れタスク',
|
|
'dash.upcomingMeetings': '近日中のミーティング',
|
|
'dash.recentActivity': '最近のアクティビティ',
|
|
'dash.empty': 'ありません',
|
|
'dash.noUnread': '未読はありません',
|
|
'dash.openNotifications': '通知一覧を開く',
|
|
'dash.due': '期限',
|
|
};
|
|
|
|
export const dictionary: Record<Locale, typeof en> = { en, ja };
|
|
|
|
export type MessageKey = keyof typeof en;
|