feat: add i18n framework with Indonesian translation support (#124)

* feat: add i18n support with separate translation files

- Extract translations from store.js to separate files for easier management
- Add translation files for English (en.js), Indonesian (id.js), Turkish (tr.js), and Chinese (zh.js)
- Load translations via window.translations object before Alpine store initialization
- Add Bahasa Indonesia option to language selector

* feat: translate remaining hardcoded UI strings

- Update index.html to use t() for Menu and GitHub labels
- Update views to translate Tier, Quota, Live, tier badges, and close button
- Update components to use translated error messages and confirmation dialogs
- Update utils to use translated validation and error messages
- Update app-init.js to use translated OAuth success/error messages
This commit is contained in:
Irvan Fauziansyah
2026-01-15 22:33:38 +07:00
committed by GitHub
parent 9ffb83ab74
commit e2d03f9b25
17 changed files with 1413 additions and 890 deletions

View File

@@ -57,8 +57,8 @@
<th class="pl-6 py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-16" x-text="$store.global.t('enabled')">Enabled</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider flex-1 min-w-[200px]" x-text="$store.global.t('accountEmail')">Account (Email)</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-20" x-text="$store.global.t('source')">Source</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-16">Tier</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-32">Quota</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-16" x-text="$store.global.t('tier')">Tier</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-32" x-text="$store.global.t('quota')">Quota</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-24" x-text="$store.global.t('health')">Health</th>
<th class="py-3 pr-6 text-right text-[10px] font-bold text-gray-500 uppercase tracking-wider w-32" x-text="$store.global.t('operations')">Operations</th>
</tr>
@@ -265,7 +265,7 @@
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
<button x-text="$store.global.t('close')">close</button>
</form>
</dialog>
@@ -323,7 +323,7 @@
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
<button x-text="$store.global.t('close')">close</button>
</form>
</dialog>
</div>

View File

@@ -20,7 +20,7 @@
<span class="absolute w-1.5 h-1.5 bg-neon-green rounded-full animate-ping opacity-75"></span>
<span class="relative w-1.5 h-1.5 bg-neon-green rounded-full"></span>
</div>
<span class="text-[10px] font-mono text-gray-500 uppercase tracking-wider">Live</span>
<span class="text-[10px] font-mono text-gray-500 uppercase tracking-wider" x-text="$store.global.t('live')">Live</span>
<span class="text-gray-700"></span>
<span class="text-[10px] font-mono text-gray-400 tabular-nums"
x-text="new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', second: '2-digit'})">
@@ -78,17 +78,17 @@
<div class="flex items-center gap-1 mt-2 text-[10px] font-mono flex-wrap" x-show="stats.subscription">
<template x-if="stats.subscription?.ultra > 0">
<span class="px-1.5 py-0.5 rounded bg-yellow-500/10 text-yellow-400 border border-yellow-500/30">
<span x-text="stats.subscription.ultra"></span> Ultra
<span x-text="stats.subscription.ultra"></span> <span x-text="$store.global.t('tierUltra')">Ultra</span>
</span>
</template>
<template x-if="stats.subscription?.pro > 0">
<span class="px-1.5 py-0.5 rounded bg-blue-500/10 text-blue-400 border border-blue-500/30">
<span x-text="stats.subscription.pro"></span> Pro
<span x-text="stats.subscription.pro"></span> <span x-text="$store.global.t('tierPro')">Pro</span>
</span>
</template>
<template x-if="stats.subscription?.free > 0">
<span class="px-1.5 py-0.5 rounded bg-gray-500/10 text-gray-400 border border-gray-500/30">
<span x-text="stats.subscription.free"></span> Free
<span x-text="stats.subscription.free"></span> <span x-text="$store.global.t('tierFree')">Free</span>
</span>
</template>
</div>

View File

@@ -213,7 +213,7 @@
<!-- Account Status Indicators -->
<div class="flex flex-wrap gap-1 justify-start max-w-[200px]" x-data="{ maxVisible: 12 }">
<template x-if="!row.quotaInfo || row.quotaInfo.length === 0">
<div class="text-[10px] text-gray-600 italic">No data</div>
<div class="text-[10px] text-gray-600 italic" x-text="$store.global.t('noData')">No data</div>
</template>
<template x-if="row.quotaInfo && row.quotaInfo.length > 0">
<div class="flex flex-wrap gap-1 justify-start">

View File

@@ -78,6 +78,7 @@
<option value="en">English</option>
<option value="zh">中文</option>
<option value="tr">Türkçe</option>
<option value="id">Bahasa Indonesia</option>
</select>
</div>