mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
feat(i18n): add Indonesian (Bahasa Indonesia) language support (#1399)
- Add 'id' to Language type in translations.ts - Add ~1000 Indonesian translation keys covering all UI sections - Update LanguageContext to persist 'id' in localStorage - Add ID button to Header.tsx language toggle - Add �� option to HeaderBar.tsx desktop dropdown and mobile toggle - Add Indonesian translations to inline text objects in LoginRequiredOverlay, StrategyMarketPage, PositionHistory Closes #XX
This commit is contained in:
committed by
GitHub
parent
285053b7a4
commit
3358c5a53e
@@ -57,6 +57,17 @@ export function Header({ simple = false }: HeaderProps) {
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setLanguage('id')}
|
||||
className="px-3 py-1.5 rounded text-xs font-semibold transition-all"
|
||||
style={
|
||||
language === 'id'
|
||||
? { background: '#F0B90B', color: '#000' }
|
||||
: { background: 'transparent', color: '#848E9C' }
|
||||
}
|
||||
>
|
||||
ID
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -99,8 +99,8 @@ export default function HeaderBar({
|
||||
{(() => {
|
||||
// Define all navigation tabs
|
||||
const navTabs: { page: Page; path: string; label: string; requiresAuth: boolean }[] = [
|
||||
{ page: 'data', path: '/data', label: language === 'zh' ? '数据' : 'Data', requiresAuth: false },
|
||||
{ page: 'strategy-market', path: '/strategy-market', label: language === 'zh' ? '策略市场' : 'Market', requiresAuth: true },
|
||||
{ page: 'data', path: '/data', label: language === 'zh' ? '数据' : language === 'id' ? 'Data' : 'Data', requiresAuth: false },
|
||||
{ page: 'strategy-market', path: '/strategy-market', label: language === 'zh' ? '策略市场' : language === 'id' ? 'Pasar' : 'Market', requiresAuth: true },
|
||||
{ page: 'traders', path: '/traders', label: t('configNav', language), requiresAuth: true },
|
||||
{ page: 'trader', path: '/dashboard', label: t('dashboardNav', language), requiresAuth: true },
|
||||
{ page: 'strategy', path: '/strategy', label: t('strategyNav', language), requiresAuth: true },
|
||||
@@ -259,7 +259,7 @@ export default function HeaderBar({
|
||||
className="flex items-center gap-2 px-3 py-2 rounded transition-colors text-nofx-text-muted hover:bg-white/5"
|
||||
>
|
||||
<span className="text-lg">
|
||||
{language === 'zh' ? '🇨🇳' : '🇺🇸'}
|
||||
{language === 'zh' ? '🇨🇳' : language === 'id' ? '🇮🇩' : '🇺🇸'}
|
||||
</span>
|
||||
<ChevronDown className="w-4 h-4" />
|
||||
</button>
|
||||
@@ -288,6 +288,17 @@ export default function HeaderBar({
|
||||
<span className="text-base">🇺🇸</span>
|
||||
<span className="text-sm">English</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
onLanguageChange?.('id')
|
||||
setLanguageDropdownOpen(false)
|
||||
}}
|
||||
className={`w-full flex items-center gap-2 px-3 py-2 transition-colors text-nofx-text-muted hover:text-white
|
||||
${language === 'id' ? 'bg-nofx-gold/10' : 'hover:bg-white/5'}`}
|
||||
>
|
||||
<span className="text-base">🇮🇩</span>
|
||||
<span className="text-sm">Bahasa</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -329,8 +340,8 @@ export default function HeaderBar({
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
{(() => {
|
||||
const navTabs: { page: Page; path: string; label: string; requiresAuth: boolean }[] = [
|
||||
{ page: 'data', path: '/data', label: language === 'zh' ? '数据' : 'Data', requiresAuth: false },
|
||||
{ page: 'strategy-market', path: '/strategy-market', label: language === 'zh' ? '策略市场' : 'Market', requiresAuth: true },
|
||||
{ page: 'data', path: '/data', label: language === 'zh' ? '数据' : language === 'id' ? 'Data' : 'Data', requiresAuth: false },
|
||||
{ page: 'strategy-market', path: '/strategy-market', label: language === 'zh' ? '策略市场' : language === 'id' ? 'Pasar' : 'Market', requiresAuth: true },
|
||||
{ page: 'traders', path: '/traders', label: t('configNav', language), requiresAuth: true },
|
||||
{ page: 'trader', path: '/dashboard', label: t('dashboardNav', language), requiresAuth: true },
|
||||
{ page: 'strategy', path: '/strategy', label: t('strategyNav', language), requiresAuth: true },
|
||||
@@ -429,7 +440,7 @@ export default function HeaderBar({
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Lang Switcher */}
|
||||
<div className="flex bg-zinc-900 rounded-lg p-1 border border-zinc-800">
|
||||
{['zh', 'en'].map((lang) => (
|
||||
{['zh', 'en', 'id'].map((lang) => (
|
||||
<button
|
||||
key={lang}
|
||||
onClick={() => {
|
||||
@@ -441,7 +452,7 @@ export default function HeaderBar({
|
||||
: 'text-zinc-500'
|
||||
}`}
|
||||
>
|
||||
{lang === 'zh' ? 'CN' : 'EN'}
|
||||
{lang === 'zh' ? 'CN' : lang === 'id' ? 'ID' : 'EN'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,20 @@ export function LoginRequiredOverlay({ isOpen, onClose, featureName }: LoginRequ
|
||||
login: 'EXECUTE LOGIN',
|
||||
register: 'REGISTER NEW ID',
|
||||
later: 'ABORT'
|
||||
},
|
||||
id: {
|
||||
title: 'AKSES SISTEM DITOLAK',
|
||||
subtitle: featureName ? `Modul "${featureName}" memerlukan hak akses lebih tinggi` : 'Otorisasi diperlukan untuk modul ini',
|
||||
description: 'Inisialisasi protokol autentikasi untuk membuka kemampuan sistem penuh: konfigurasi Trader AI, aliran data Pasar Strategi, dan inti Simulasi Backtest.',
|
||||
benefits: [
|
||||
'Kontrol Trader AI',
|
||||
'Pasar Strategi HFT',
|
||||
'Mesin Backtest Historis',
|
||||
'Visualisasi Sistem Penuh'
|
||||
],
|
||||
login: 'JALANKAN LOGIN',
|
||||
register: 'DAFTAR ID BARU',
|
||||
later: 'BATALKAN'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { api } from '../lib/api'
|
||||
import { useLanguage } from '../contexts/LanguageContext'
|
||||
import { t } from '../i18n/translations'
|
||||
import { t, type Language } from '../i18n/translations'
|
||||
import { MetricTooltip } from './MetricTooltip'
|
||||
import { formatPrice, formatQuantity } from '../utils/format'
|
||||
import type {
|
||||
@@ -152,7 +152,7 @@ function SymbolStatsRow({ stat }: { stat: SymbolStats }) {
|
||||
}
|
||||
|
||||
// Direction Stats Card
|
||||
function DirectionStatsCard({ stat, language }: { stat: DirectionStats; language: 'en' | 'zh' }) {
|
||||
function DirectionStatsCard({ stat, language }: { stat: DirectionStats; language: Language }) {
|
||||
const isLong = (stat.side || '').toLowerCase() === 'long'
|
||||
const iconColor = isLong ? '#0ECB81' : '#F6465D'
|
||||
const totalPnl = stat.total_pnl || 0
|
||||
|
||||
@@ -14,7 +14,7 @@ export function LanguageProvider({ children }: { children: ReactNode }) {
|
||||
// Initialize language from localStorage or default to English
|
||||
const [language, setLanguage] = useState<Language>(() => {
|
||||
const saved = localStorage.getItem('language')
|
||||
return saved === 'en' || saved === 'zh' ? saved : 'en'
|
||||
return saved === 'en' || saved === 'zh' || saved === 'id' ? saved : 'en'
|
||||
})
|
||||
|
||||
// Save language to localStorage whenever it changes
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -158,6 +158,32 @@ export function StrategyMarketPage() {
|
||||
shareYours: 'UPLOAD_STRATEGY',
|
||||
makePublic: 'PUBLISH',
|
||||
loading: 'INITIALIZING...'
|
||||
},
|
||||
id: {
|
||||
title: 'PASAR STRATEGI',
|
||||
subtitle: 'DATABASE STRATEGI GLOBAL',
|
||||
description: 'Temukan, analisis, dan kloning algoritma trading berperforma tinggi',
|
||||
search: 'CARI PARAMETER...',
|
||||
all: 'SEMUA PROTOKOL',
|
||||
popular: 'TREN',
|
||||
recent: 'TERBARU',
|
||||
myStrategies: 'PERPUSTAKAAN SAYA',
|
||||
noStrategies: 'TIDAK ADA SINYAL',
|
||||
noStrategiesDesc: 'Tidak ada sinyal strategis terdeteksi pada frekuensi ini',
|
||||
author: 'OPERATOR',
|
||||
createdAt: 'TIMESTAMP',
|
||||
viewConfig: 'DEKRIPSI CONFIG',
|
||||
hideConfig: 'ENKRIPSI',
|
||||
copyConfig: 'KLON CONFIG',
|
||||
copied: 'DISALIN',
|
||||
configHidden: 'TERENKRIPSI',
|
||||
configHiddenDesc: 'Parameter konfigurasi terenkripsi',
|
||||
indicators: 'INDIKATOR',
|
||||
maxPositions: 'BATAS_POS',
|
||||
maxLeverage: 'LEV_MAKS',
|
||||
shareYours: 'UNGGAH_STRATEGI',
|
||||
makePublic: 'PUBLIKASI',
|
||||
loading: 'MENGINISIALISASI...'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user