Commit 8f31b305 by CaIon

fix(i18n): standardize locale formatting for Intl APIs

parent 5cbb7b0b
......@@ -39,7 +39,6 @@ export function LanguageSwitcher() {
const { i18n, t } = useTranslation()
const user = useAuthStore((s) => s.auth.user)
const currentLanguage = normalizeInterfaceLanguage(i18n.language)
console.log('Current interface language: %s', currentLanguage)
const handleChangeLanguage = useCallback(
async (code: string) => {
await i18n.changeLanguage(code)
......
......@@ -51,6 +51,7 @@ import {
formatQuotaWithCurrency,
getCurrencyLabel,
} from '@/lib/currency'
import { toIntlLocale } from '@/i18n/languages'
import { formatTimestampToDate } from '@/lib/format'
import { truncateText } from '@/lib/utils'
......@@ -310,7 +311,7 @@ function BalanceCell({ channel }: { channel: Channel }) {
const withSuffix = (value: string) =>
tokenSuffix && value !== '-' ? `${value}${tokenSuffix}` : value
const locale = i18n.resolvedLanguage || i18n.language
const locale = toIntlLocale(i18n.resolvedLanguage || i18n.language)
const balanceFormatOptions = {
digitsLarge: 2,
digitsSmall: 4,
......@@ -519,7 +520,7 @@ export function useChannelsColumns(
const { t, i18n } = useTranslation()
const { sensitiveVisible } = useChannels()
const enableSelection = options.enableSelection ?? true
const locale = i18n.resolvedLanguage || i18n.language
const locale = toIntlLocale(i18n.resolvedLanguage || i18n.language)
// The column definitions only depend on the translation function, the active
// locale, and sensitive-data visibility. Memoizing keeps the array (and every
// cell renderer reference) stable across unrelated re-renders, so react-table
......
......@@ -31,6 +31,7 @@ import type {
QuotaDataItem,
DashboardFilters,
} from '@/features/dashboard/types'
import { toIntlLocale } from '@/i18n/languages'
import { formatCompactNumber, formatNumber, formatQuota } from '@/lib/format'
import { computeTimeRange } from '@/lib/time'
import { cn } from '@/lib/utils'
......@@ -121,7 +122,7 @@ export function LogStatCards(props: LogStatCardsProps) {
const items = statCardsConfig.map((config) => {
const rawValue = config.getValue(adaptedStats, timeRangeMinutes)
const locale = i18n.resolvedLanguage || i18n.language
const locale = toIntlLocale(i18n.resolvedLanguage || i18n.language)
const formatted =
config.key === 'quota'
? {
......
......@@ -47,6 +47,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { toIntlLocale } from '@/i18n/languages'
import { formatTimestampRelative, formatTimestampToDate } from '@/lib/format'
import { cn } from '@/lib/utils'
......@@ -411,7 +412,7 @@ function SystemInstancesList(props: SystemInstancesTableProps) {
{formatTimestampRelative(
instance.last_seen_at,
'seconds',
i18n.language
toIntlLocale(i18n.language)
)}
</TableCell>
</TableRow>
......
......@@ -38,6 +38,7 @@ import type {
SystemTask,
SystemTaskStatus,
} from '@/features/system-settings/types'
import { toIntlLocale } from '@/i18n/languages'
import { formatTimestampRelative, formatTimestampToDate } from '@/lib/format'
import { cn } from '@/lib/utils'
......@@ -185,7 +186,7 @@ function SystemTasksTable(props: SystemTasksTableProps) {
{formatTimestampRelative(
task.updated_at,
'seconds',
i18n.language
toIntlLocale(i18n.language)
)}
</TableCell>
<TableCell
......
......@@ -41,8 +41,33 @@ export function normalizeInterfaceLanguage(value?: string | null): string {
normalized = 'zhCN'
}
console.log('Normalized interface language: %s -> %s', value, normalized)
return INTERFACE_LANGUAGE_OPTIONS.some((lang) => lang.code === normalized)
? normalized
: 'en'
}
/**
* Convert an interface language code (the values i18next uses, such as `zhCN` /
* `zhTW`) into a valid BCP-47 locale tag that the `Intl.*` APIs accept.
*
* `new Intl.NumberFormat('zhCN')` throws `RangeError: Invalid language tag`, so
* any locale derived from `i18n.language` / `i18n.resolvedLanguage` MUST be run
* through this before it reaches an `Intl` constructor. Unknown values fall back
* to `undefined`, which makes `Intl` use the runtime default locale.
*/
export function toIntlLocale(value?: string | null): string | undefined {
if (!value) return undefined
switch (value) {
case 'zhCN':
return 'zh-CN'
case 'zhTW':
return 'zh-TW'
default:
break
}
try {
return Intl.getCanonicalLocales(value)[0]
} catch {
return undefined
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment