Skip to content

Commit e4f9574

Browse files
feat: remove Japanese language support (English-only)
- Remove Japanese translations from lib/i18n/translations.ts - Simplify I18nProvider to English-only in lib/i18n/context.tsx - Remove locale state from settingsSlice - Remove language selector UI from Settings page - Update middleware tests for English-only locale - Net reduction: ~253 lines of code
1 parent 50ebd11 commit e4f9574

File tree

6 files changed

+33
-286
lines changed

6 files changed

+33
-286
lines changed

app/settings/SettingsClient.tsx

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
'use client'
88

9-
import { Check, Sun, Moon, Monitor, Globe } from 'lucide-react'
9+
import { Check, Sun, Moon, Monitor } from 'lucide-react'
1010
import Link from 'next/link'
1111
import { useRouter } from 'next/navigation'
1212
import { useState, memo, useMemo, useCallback } from 'react'
@@ -88,13 +88,6 @@ const THEME_BUTTON_SELECTED =
8888
const THEME_BUTTON_UNSELECTED =
8989
'border-border hover:border-primary/50 hover:bg-muted/50'
9090

91-
/** Base styles for language button */
92-
const LANGUAGE_BUTTON_BASE =
93-
'flex items-center gap-2 rounded-lg border-2 px-4 py-3 transition-all'
94-
const LANGUAGE_BUTTON_SELECTED =
95-
'border-primary bg-primary/5 ring-2 ring-primary ring-offset-2'
96-
const LANGUAGE_BUTTON_UNSELECTED = 'border-border hover:border-primary/50'
97-
9891
const THEME_INFO: Record<
9992
ThemeType,
10093
{ name: string; color: string; description: string }
@@ -169,7 +162,7 @@ const ThemeButton = memo(function ThemeButton({
169162
export const SettingsClient = memo(function SettingsClient() {
170163
const router = useRouter()
171164
const { theme, setTheme, mounted } = useTheme()
172-
const { language, setLanguage, t } = useI18n()
165+
const { t } = useI18n()
173166

174167
/**
175168
* Handles the save settings action.
@@ -182,18 +175,6 @@ export const SettingsClient = memo(function SettingsClient() {
182175
router.push('/boards')
183176
}, [router])
184177

185-
const englishButtonClassName = useMemo(
186-
() =>
187-
`${LANGUAGE_BUTTON_BASE} ${language === 'en' ? LANGUAGE_BUTTON_SELECTED : LANGUAGE_BUTTON_UNSELECTED}`,
188-
[language],
189-
)
190-
191-
const japaneseButtonClassName = useMemo(
192-
() =>
193-
`${LANGUAGE_BUTTON_BASE} ${language === 'ja' ? LANGUAGE_BUTTON_SELECTED : LANGUAGE_BUTTON_UNSELECTED}`,
194-
[language],
195-
)
196-
197178
if (!mounted) {
198179
return (
199180
<div className="container mx-auto max-w-4xl px-4 py-8">
@@ -349,51 +330,6 @@ export const SettingsClient = memo(function SettingsClient() {
349330
</CardContent>
350331
</Card>
351332

352-
{/* Language Settings */}
353-
<Card>
354-
<CardHeader>
355-
<CardTitle className="flex items-center gap-2">
356-
<Globe className="h-5 w-5" />
357-
Language / 言語
358-
</CardTitle>
359-
<CardDescription>Choose your preferred language</CardDescription>
360-
</CardHeader>
361-
<CardContent>
362-
<div className="flex gap-3">
363-
<button
364-
type="button"
365-
onClick={() => setLanguage('en')}
366-
className={englishButtonClassName}
367-
>
368-
<span className="text-2xl">🇺🇸</span>
369-
<div className="text-left">
370-
<span className="block font-medium">English</span>
371-
<span className="text-xs text-muted-foreground">English</span>
372-
</div>
373-
{language === 'en' && (
374-
<Check className="ml-2 h-4 w-4 text-primary" />
375-
)}
376-
</button>
377-
<button
378-
type="button"
379-
onClick={() => setLanguage('ja')}
380-
className={japaneseButtonClassName}
381-
>
382-
<span className="text-2xl">🇯🇵</span>
383-
<div className="text-left">
384-
<span className="block font-medium">日本語</span>
385-
<span className="text-xs text-muted-foreground">
386-
Japanese
387-
</span>
388-
</div>
389-
{language === 'ja' && (
390-
<Check className="ml-2 h-4 w-4 text-primary" />
391-
)}
392-
</button>
393-
</div>
394-
</CardContent>
395-
</Card>
396-
397333
{/* Actions */}
398334
<div className="flex justify-between pt-4">
399335
<Link href="/boards">

lib/i18n/context.tsx

Lines changed: 26 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,69 @@
11
/**
22
* Internationalization Context
33
*
4-
* Provides language switching and translation access throughout the app
4+
* Provides translation access throughout the app (English-only)
55
*/
66

77
'use client'
88

9-
import React, {
10-
createContext,
11-
useContext,
12-
useState,
13-
useCallback,
14-
useMemo,
15-
useSyncExternalStore,
16-
memo,
17-
} from 'react'
9+
import React, { createContext, useContext, useMemo, memo } from 'react'
1810

19-
import type { Language } from './translations'
2011
import { translations } from './translations'
2112

22-
const LANGUAGE_STORAGE_KEY = 'gitbox-language'
23-
2413
interface I18nContextType {
25-
language: Language
26-
setLanguage: (lang: Language) => void
27-
t: (typeof translations)[Language]
14+
t: typeof translations.en
2815
}
2916

3017
const I18nContext = createContext<I18nContextType | undefined>(undefined)
3118

32-
// Helper to detect if we're in a browser environment
33-
const getSnapshot = () => true
34-
const getServerSnapshot = () => false
35-
const subscribe = () => () => {}
36-
19+
/**
20+
* Provider component that supplies English translations to the app.
21+
* @param children - React children to wrap with i18n context
22+
* @returns Provider component with English translations
23+
*/
3724
export const I18nProvider = memo(function I18nProvider({
3825
children,
3926
}: {
4027
children: React.ReactNode
4128
}): React.ReactNode {
42-
const isClient = useSyncExternalStore(
43-
subscribe,
44-
getSnapshot,
45-
getServerSnapshot,
46-
)
47-
48-
// Initialize language from localStorage (client-side only)
49-
const [language, setLanguageState] = useState<Language>(() => {
50-
if (typeof window === 'undefined') return 'en'
51-
const stored = localStorage.getItem(LANGUAGE_STORAGE_KEY) as Language | null
52-
if (stored && (stored === 'en' || stored === 'ja')) {
53-
return stored
54-
}
55-
const browserLang = navigator.language.split('-')[0]
56-
return browserLang === 'ja' ? 'ja' : 'en'
57-
})
58-
59-
const setLanguage = useCallback((lang: Language) => {
60-
setLanguageState(lang)
61-
localStorage.setItem(LANGUAGE_STORAGE_KEY, lang)
62-
document.documentElement.lang = lang
63-
}, [])
64-
65-
// Memoize context value for stable reference
6629
const contextValue = useMemo(
6730
() => ({
68-
language,
69-
setLanguage,
70-
t: translations[language],
31+
t: translations.en,
7132
}),
72-
[language, setLanguage],
33+
[],
7334
)
7435

75-
// Show children with default English until client-side hydration
76-
if (!isClient) {
77-
return children
78-
}
79-
8036
return (
8137
<I18nContext.Provider value={contextValue}>{children}</I18nContext.Provider>
8238
)
8339
})
8440

41+
/**
42+
* Hook to access i18n context with translations.
43+
* @returns Object containing English translations
44+
* @example
45+
* const { t } = useI18n()
46+
* return <h1>{t.common.loading}</h1>
47+
*/
8548
export function useI18n() {
8649
const context = useContext(I18nContext)
8750
if (!context) {
8851
// Return default values for SSR/pre-render
8952
return {
90-
language: 'en' as Language,
91-
setLanguage: () => {},
9253
t: translations.en,
9354
}
9455
}
9556
return context
9657
}
9758

98-
// Utility hook for just getting translations (simpler API)
59+
/**
60+
* Utility hook for getting translations (simpler API).
61+
* @returns Object containing translations
62+
* @example
63+
* const { t } = useTranslation()
64+
* return <span>{t.settings.title}</span>
65+
*/
9966
export function useTranslation() {
100-
const { t, language } = useI18n()
101-
return { t, language }
67+
const { t } = useI18n()
68+
return { t }
10269
}

lib/i18n/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,5 @@
22
* i18n Module Exports
33
*/
44

5-
export {
6-
translations,
7-
type Language,
8-
type TranslationKeys,
9-
} from './translations'
5+
export { translations, type TranslationKeys } from './translations'
106
export { I18nProvider, useI18n, useTranslation } from './context'

lib/i18n/translations.ts

Lines changed: 1 addition & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Internationalization Translations
33
*
4-
* PRD: 対応言語 - 英語、日本語
4+
* English-only translation system
55
*/
66

77
export const translations = {
@@ -145,146 +145,6 @@ export const translations = {
145145
followSystem: 'Follow system preference',
146146
},
147147
},
148-
ja: {
149-
common: {
150-
loading: '読み込み中...',
151-
error: 'エラー',
152-
save: '保存',
153-
cancel: 'キャンセル',
154-
delete: '削除',
155-
edit: '編集',
156-
create: '作成',
157-
back: '戻る',
158-
signIn: 'ログイン',
159-
signOut: 'ログアウト',
160-
search: '検索',
161-
settings: '設定',
162-
help: 'ヘルプ',
163-
},
164-
nav: {
165-
boards: 'ボード',
166-
allBoards: 'すべてのボード',
167-
favorites: 'お気に入り',
168-
maintenance: 'メンテナンスモード',
169-
settings: '設定',
170-
shortcuts: 'ショートカット',
171-
},
172-
landing: {
173-
title: 'GitHubリポジトリを今までにない方法で管理',
174-
subtitle: 'GitHubリポジトリを整理するための美しいカンバンボード',
175-
getStarted: '始める',
176-
signIn: 'GitHubでログイン',
177-
},
178-
boards: {
179-
title: 'マイボード',
180-
description: 'GitHubリポジトリをカンバン形式で管理',
181-
createBoard: 'ボードを作成',
182-
noBoards: 'ボードがありません',
183-
noBoardsDescription: '最初のボードを作成して始めましょう',
184-
},
185-
board: {
186-
addColumn: '列を追加',
187-
settings: 'ボード設定',
188-
wipLimit: 'WIP制限',
189-
wipExceeded: 'WIP制限を超えています',
190-
},
191-
card: {
192-
openGitHub: 'GitHubで開く',
193-
openProduction: '本番URLを開く',
194-
openTracking: 'トラッキングダッシュボードを開く',
195-
openSupabase: 'Supabaseダッシュボードを開く',
196-
editProjectInfo: 'プロジェクト情報を編集…',
197-
moveToMaintenance: 'メンテナンスに移動',
198-
restoreToBoard: 'ボードに復元',
199-
},
200-
maintenance: {
201-
title: 'メンテナンスモード',
202-
description: 'アーカイブ済み・保守中のプロジェクト',
203-
items: '件',
204-
noProjects: 'メンテナンスプロジェクトがありません',
205-
noProjectsDescription:
206-
'ボードからプロジェクトをここに移動してアーカイブできます',
207-
searchRepos: 'リポジトリを検索...',
208-
},
209-
settings: {
210-
title: '設定',
211-
description: 'GitBoxをカスタマイズ',
212-
theme: 'テーマ',
213-
themeDescription: 'お好みのテーマを選択してください',
214-
system: 'システム',
215-
lightThemes: 'ライトテーマ',
216-
darkThemes: 'ダークテーマ',
217-
display: '表示',
218-
displayDescription: 'コンテンツの表示方法を設定',
219-
compactMode: 'コンパクトモード',
220-
compactModeDescription: '間隔とカードサイズを縮小',
221-
showMetadata: 'カードメタデータを表示',
222-
showMetadataDescription: 'スター数、言語、最終更新日を表示',
223-
wipWarnings: 'WIP制限警告',
224-
wipWarningsDescription: 'WIP制限超過時に警告を表示',
225-
typography: 'タイポグラフィ',
226-
typographyDescription: '読みやすさのためにテキストサイズを調整',
227-
fontSize: '基本フォントサイズ',
228-
backToBoards: 'ボードに戻る',
229-
saveSettings: '設定を保存',
230-
},
231-
commandPalette: {
232-
searchCommands: 'コマンドを検索...',
233-
navigation: 'ナビゲーション',
234-
actions: 'アクション',
235-
goToBoards: 'ボードへ移動',
236-
goToMaintenance: 'メンテナンスへ移動',
237-
goToSettings: '設定へ移動',
238-
createNewBoard: '新規ボード作成',
239-
changeTheme: 'テーマを変更',
240-
keyboardShortcuts: 'キーボードショートカット',
241-
helpDocs: 'ヘルプとドキュメント',
242-
},
243-
createBoard: {
244-
title: '新規ボード作成',
245-
description: 'ボード名を入力してテーマを選択してください',
246-
boardName: 'ボード名',
247-
boardNamePlaceholder: '例: AIプロジェクト, サイドプロジェクト',
248-
theme: 'テーマ',
249-
light: 'ライト',
250-
dark: 'ダーク',
251-
creating: '作成中...',
252-
},
253-
projectInfo: {
254-
title: 'プロジェクト情報',
255-
note: 'ノート',
256-
links: 'リンク',
257-
credentials: '認証情報',
258-
addLink: 'リンクを追加',
259-
addCredential: '認証情報を追加',
260-
},
261-
statusList: {
262-
addColumn: '新しい列を追加',
263-
editColumn: '列を編集',
264-
name: '名前',
265-
color: '色',
266-
wipLimit: 'WIP制限 (オプション)',
267-
wipLimitDescription:
268-
'作業中アイテムの制限。制限なしの場合は空にしてください。',
269-
saveChanges: '変更を保存',
270-
},
271-
themes: {
272-
sunrise: 'サンライズ',
273-
sandstone: 'サンドストーン',
274-
mint: 'ミント',
275-
sky: 'スカイ',
276-
lavender: 'ラベンダー',
277-
rose: 'ローズ',
278-
midnight: 'ミッドナイト',
279-
graphite: 'グラファイト',
280-
forest: 'フォレスト',
281-
ocean: 'オーシャン',
282-
plum: 'プラム',
283-
rust: 'ラスト',
284-
followSystem: 'システム設定に従う',
285-
},
286-
},
287148
} as const
288149

289-
export type Language = keyof typeof translations
290150
export type TranslationKeys = typeof translations.en

0 commit comments

Comments
 (0)