diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d3ed4ca --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + check-locales: + name: Check Locale Parity + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Check Locales Parity + run: node scripts/check-locales.js diff --git a/messages/es.json b/messages/es.json index 871397f..35f8af3 100644 --- a/messages/es.json +++ b/messages/es.json @@ -130,7 +130,7 @@ }, "LoanCard": { "totalOwed": "Total adeudado", - "principal": "Principal", + "principal": "Capital", "accruedInterest": "Interés acumulado", "interest": "Interés", "nextPayment": "Próximo pago", diff --git a/messages/tl.json b/messages/tl.json index 5f2fb85..67e7674 100644 --- a/messages/tl.json +++ b/messages/tl.json @@ -5,10 +5,10 @@ "getStarted": "Magsimula Na", "learnMore": "Matuto Nang Higit Pa", "stats": { - "netWorth": "Net Worth", + "netWorth": "Netong Halaga", "activeLoans": "Mga aktibong loan", "totalRemitted": "Kabuuang naipadala", - "yieldApy": "Yield (APY)" + "yieldApy": "Kita (APY)" }, "activity": { "title": "Kamakailang aktibidad", @@ -23,7 +23,7 @@ "sendRemittanceDesc": "Maglipat ng pondo sa buong mundo" }, "outreach": { - "title": "Community Outreach", + "title": "Programa sa Komunidad", "description": "Ang mga bagong borrower sa Ghana ay naghahanap ng mga micro-loan para sa mga kagamitang pang-agrikultura. Tulungang palaguin ang ecosystem!", "explore": "Galugarin ang mga pagkakataon" } @@ -46,7 +46,7 @@ "completed": "Nakumpleto", "repaid": "Binayaran", "failed": "Nabigo", - "liquidated": "Liquidated", + "liquidated": "Na-liquidate", "defaulted": "Di-nakamit" }, "pagination": { @@ -60,7 +60,7 @@ "amount": "Halaga", "status": "Katayuan", "transactionHash": "Hash ng Transaksyon", - "stellarExplorerLink": "Stellar Explorer Link" + "stellarExplorerLink": "Link ng Stellar Explorer" } }, "WalletPage": { @@ -68,14 +68,14 @@ "date": "Petsa", "type": "Uri", "amount": "Halaga", - "asset": "Asset", + "asset": "Ari-arian", "status": "Katayuan", "transactionHash": "Hash ng Transaksyon", - "stellarExplorerLink": "Stellar Explorer Link" + "stellarExplorerLink": "Link ng Stellar Explorer" } }, "Navigation": { - "home": "Home", + "home": "Pangunahin", "loans": "Mga Loan", "repay": "Bayaran", "dashboard": "Dashboard", @@ -98,7 +98,7 @@ "all": "Lahat", "active": "Aktibo", "repaid": "Bayad Na", - "defaulted": "Defaulted" + "defaulted": "Hindi Nabayaran" }, "empty": { "title": "Walang nahanap na loan", @@ -110,7 +110,7 @@ "next": "Susunod", "pageOf": "Pahina {current} of {total}" }, - "loanNumber": "Loan #{id}", + "loanNumber": "Utang #{id}", "due": "Dapat bayaran sa {date}", "status": { "active": "Aktibo", @@ -118,7 +118,7 @@ } }, "Kingdom": { - "title": "Kingdom Dashboard", + "title": "Dashboard ng Kaharian", "description": "Subaybayan ang iyong pag-unlad, i-unlock ang mga tagumpay, at umangat sa mga ranggo", "welcome": "Maligayang pagdating, {kingdomTitle}!", "level": "Ngayon ay nasa Level {level} ka", @@ -130,7 +130,7 @@ }, "LoanCard": { "totalOwed": "Kabuuang Utang", - "principal": "Principal", + "principal": "Pangunahing Halaga", "accruedInterest": "Naipon na Interes", "interest": "Interes", "nextPayment": "Susunod na Pagbabayad", @@ -140,7 +140,7 @@ "onTrack": "Sa Track", "dueSoon": "Malapit nang Dapat Bayaran", "overdue": "Lintasan", - "defaulted": "Defaulted", + "defaulted": "Hindi Nabayaran", "repayNow": "Bayaran Ngayon", "payNowOverdue": "Bayaran Ngayon (Lintasan)", "contactSupport": "Kontakin ang Support", diff --git a/package.json b/package.json index 9f2c8e1..5cb4637 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "format": "prettier --write .", "test:e2e": "playwright test", "prepare": "husky", - "analyze": "ANALYZE=true next build" + "analyze": "ANALYZE=true next build", + "check-locales": "node scripts/check-locales.js" }, "dependencies": { "@sentry/nextjs": "^10.46.0", diff --git a/scripts/check-locales.js b/scripts/check-locales.js new file mode 100644 index 0000000..d168e37 --- /dev/null +++ b/scripts/check-locales.js @@ -0,0 +1,81 @@ +const fs = require("fs"); +const path = require("path"); + +const MESSAGES_DIR = path.join(__dirname, "../messages"); +const EN_FILE = path.join(MESSAGES_DIR, "en.json"); +const OTHER_LOCALES = ["es.json", "tl.json"]; + +function flattenObject(ob) { + let toReturn = {}; + for (let i in ob) { + if (!ob.hasOwnProperty(i)) continue; + + if (typeof ob[i] == "object" && ob[i] !== null) { + let flatObject = flattenObject(ob[i]); + for (let x in flatObject) { + if (!flatObject.hasOwnProperty(x)) continue; + toReturn[i + "." + x] = flatObject[x]; + } + } else { + toReturn[i] = ob[i]; + } + } + return toReturn; +} + +function checkLocales() { + console.log("Checking locale drift..."); + + if (!fs.existsSync(EN_FILE)) { + console.error("Error: en.json not found"); + process.exit(1); + } + + const enContent = JSON.parse(fs.readFileSync(EN_FILE, "utf8")); + const enKeys = Object.keys(flattenObject(enContent)).sort(); + + let hasErrors = false; + + OTHER_LOCALES.forEach((localeFile) => { + const localePath = path.join(MESSAGES_DIR, localeFile); + if (!fs.existsSync(localePath)) { + console.error(`Error: ${localeFile} not found`); + hasErrors = true; + return; + } + + const localeContent = JSON.parse(fs.readFileSync(localePath, "utf8")); + const localeKeys = Object.keys(flattenObject(localeContent)).sort(); + + const missingKeys = enKeys.filter((k) => !localeKeys.includes(k)); + const extraKeys = localeKeys.filter((k) => !enKeys.includes(k)); + + if (missingKeys.length > 0) { + console.error(`\n❌ [${localeFile}] Missing keys compared to en.json:`); + missingKeys.forEach((k) => console.error(` - ${k}`)); + hasErrors = true; + } + + if (extraKeys.length > 0) { + console.error(`\n❌ [${localeFile}] Extra keys not found in en.json:`); + extraKeys.forEach((k) => console.error(` - ${k}`)); + hasErrors = true; + } + + if (missingKeys.length === 0 && extraKeys.length === 0) { + console.log(`✅ ${localeFile} is in sync with en.json`); + } + }); + + if (hasErrors) { + console.error( + "\n❌ Locale drift detected! Please ensure all locale files have the exact same keys as en.json.", + ); + process.exit(1); + } else { + console.log("\n✨ All locales are perfectly in sync!"); + process.exit(0); + } +} + +checkLocales(); diff --git a/src/app/[locale]/settings/page.tsx b/src/app/[locale]/settings/page.tsx index 4d80f29..21a23f9 100644 --- a/src/app/[locale]/settings/page.tsx +++ b/src/app/[locale]/settings/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; +import { useParams, useRouter, usePathname } from "next/navigation"; import { User, Wallet, @@ -423,15 +424,25 @@ function SecuritySection() { // ─── Display section ────────────────────────────────────────────────────────── function DisplaySection() { + const router = useRouter(); + const pathname = usePathname(); + const params = useParams(); + const currentLocale = (params?.locale as string) || "en"; + const LANGUAGES = [ { code: "en", label: "English" }, { code: "es", label: "Español" }, - { code: "fr", label: "Français" }, - { code: "pt", label: "Português" }, - { code: "hi", label: "हिन्दी" }, + { code: "tl", label: "Tagalog" }, ]; - const [language, setLanguage] = useState("en"); + const handleLanguageChange = (e: React.ChangeEvent) => { + const newLocale = e.target.value; + if (pathname.startsWith(`/${currentLocale}`)) { + router.push(pathname.replace(`/${currentLocale}`, `/${newLocale}`)); + } else { + router.push(`/${newLocale}${pathname}`); + } + }; const theme = useThemeStore((s) => s.theme); const hydrated = useThemeStore((s) => s.hydrated); @@ -488,8 +499,8 @@ function DisplaySection() { -

- Full i18n support is coming soon. Only English is fully translated. -