mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-19 01:51:21 +08:00
test(vscode): extract locale normalization and template interpolation tests
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { describe, it, expect } from "bun:test"
|
||||
import { normalizeLocale, resolveTemplate } from "../../webview-ui/src/context/language-utils"
|
||||
|
||||
describe("normalizeLocale", () => {
|
||||
it("returns 'en' for English", () => {
|
||||
expect(normalizeLocale("en")).toBe("en")
|
||||
expect(normalizeLocale("en-US")).toBe("en")
|
||||
expect(normalizeLocale("en-GB")).toBe("en")
|
||||
})
|
||||
|
||||
it("returns 'zh' for Simplified Chinese", () => {
|
||||
expect(normalizeLocale("zh")).toBe("zh")
|
||||
expect(normalizeLocale("zh-CN")).toBe("zh")
|
||||
expect(normalizeLocale("zh-Hans")).toBe("zh")
|
||||
})
|
||||
|
||||
it("returns 'zht' for Traditional Chinese", () => {
|
||||
expect(normalizeLocale("zh-Hant")).toBe("zht")
|
||||
expect(normalizeLocale("zh-TW")).toBe("zh")
|
||||
expect(normalizeLocale("zh-hant-TW")).toBe("zht")
|
||||
})
|
||||
|
||||
it("returns 'de' for German", () => {
|
||||
expect(normalizeLocale("de")).toBe("de")
|
||||
expect(normalizeLocale("de-AT")).toBe("de")
|
||||
})
|
||||
|
||||
it("returns 'ko' for Korean", () => {
|
||||
expect(normalizeLocale("ko")).toBe("ko")
|
||||
expect(normalizeLocale("ko-KR")).toBe("ko")
|
||||
})
|
||||
|
||||
it("returns 'no' for Norwegian Bokmål", () => {
|
||||
expect(normalizeLocale("nb")).toBe("no")
|
||||
expect(normalizeLocale("nb-NO")).toBe("no")
|
||||
})
|
||||
|
||||
it("returns 'no' for Norwegian Nynorsk", () => {
|
||||
expect(normalizeLocale("nn")).toBe("no")
|
||||
})
|
||||
|
||||
it("returns 'br' for Portuguese", () => {
|
||||
expect(normalizeLocale("pt")).toBe("br")
|
||||
expect(normalizeLocale("pt-BR")).toBe("br")
|
||||
expect(normalizeLocale("pt-PT")).toBe("br")
|
||||
})
|
||||
|
||||
it("falls back to 'en' for unknown locale", () => {
|
||||
expect(normalizeLocale("xx")).toBe("en")
|
||||
expect(normalizeLocale("xyz-ZZ")).toBe("en")
|
||||
})
|
||||
|
||||
it("is case-insensitive", () => {
|
||||
expect(normalizeLocale("EN")).toBe("en")
|
||||
expect(normalizeLocale("DE")).toBe("de")
|
||||
expect(normalizeLocale("ZH-HANT")).toBe("zht")
|
||||
})
|
||||
})
|
||||
|
||||
describe("resolveTemplate", () => {
|
||||
it("returns text unchanged when no params", () => {
|
||||
expect(resolveTemplate("hello world")).toBe("hello world")
|
||||
})
|
||||
|
||||
it("returns text unchanged when params is undefined", () => {
|
||||
expect(resolveTemplate("no {{var}} here", undefined)).toBe("no {{var}} here")
|
||||
})
|
||||
|
||||
it("interpolates a single variable", () => {
|
||||
expect(resolveTemplate("Hello {{name}}!", { name: "World" })).toBe("Hello World!")
|
||||
})
|
||||
|
||||
it("interpolates multiple variables", () => {
|
||||
const result = resolveTemplate("{{a}} + {{b}} = {{c}}", { a: "1", b: "2", c: "3" })
|
||||
expect(result).toBe("1 + 2 = 3")
|
||||
})
|
||||
|
||||
it("replaces missing variable with empty string", () => {
|
||||
expect(resolveTemplate("{{missing}}", {})).toBe("")
|
||||
})
|
||||
|
||||
it("handles numeric variable values", () => {
|
||||
expect(resolveTemplate("count: {{n}}", { n: 42 })).toBe("count: 42")
|
||||
})
|
||||
|
||||
it("handles whitespace around key in braces", () => {
|
||||
expect(resolveTemplate("{{ name }}", { name: "test" })).toBe("test")
|
||||
})
|
||||
|
||||
it("leaves unrelated text intact", () => {
|
||||
const result = resolveTemplate("prefix {{x}} suffix", { x: "VALUE" })
|
||||
expect(result).toBe("prefix VALUE suffix")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,70 @@
|
||||
export type Locale =
|
||||
| "en"
|
||||
| "zh"
|
||||
| "zht"
|
||||
| "ko"
|
||||
| "de"
|
||||
| "es"
|
||||
| "fr"
|
||||
| "da"
|
||||
| "ja"
|
||||
| "pl"
|
||||
| "ru"
|
||||
| "ar"
|
||||
| "no"
|
||||
| "br"
|
||||
| "th"
|
||||
| "bs"
|
||||
|
||||
export const LOCALES: readonly Locale[] = [
|
||||
"en",
|
||||
"zh",
|
||||
"zht",
|
||||
"ko",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"da",
|
||||
"ja",
|
||||
"pl",
|
||||
"ru",
|
||||
"ar",
|
||||
"no",
|
||||
"br",
|
||||
"th",
|
||||
"bs",
|
||||
]
|
||||
|
||||
/**
|
||||
* Normalize a BCP 47 language tag to one of the supported Locale values.
|
||||
* Falls back to "en" for unrecognized locales.
|
||||
*/
|
||||
export function normalizeLocale(lang: string): Locale {
|
||||
const lower = lang.toLowerCase()
|
||||
if (lower.startsWith("zh")) {
|
||||
return lower.includes("hant") ? "zht" : "zh"
|
||||
}
|
||||
for (const loc of LOCALES) {
|
||||
if (lower.startsWith(loc)) {
|
||||
return loc
|
||||
}
|
||||
}
|
||||
if (lower.startsWith("nb") || lower.startsWith("nn")) {
|
||||
return "no"
|
||||
}
|
||||
if (lower.startsWith("pt")) {
|
||||
return "br"
|
||||
}
|
||||
return "en"
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform {{key}} template interpolation against a params record.
|
||||
*/
|
||||
export function resolveTemplate(text: string, params?: Record<string, string | number | boolean | undefined>): string {
|
||||
if (!params) return text
|
||||
return text.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_, rawKey) => {
|
||||
const value = params[String(rawKey)]
|
||||
return value === undefined ? "" : String(value)
|
||||
})
|
||||
}
|
||||
@@ -58,43 +58,12 @@ import { dict as kiloBr } from "@kilocode/kilo-i18n/br"
|
||||
import { dict as kiloTh } from "@kilocode/kilo-i18n/th"
|
||||
import { dict as kiloBs } from "@kilocode/kilo-i18n/bs"
|
||||
import { useVSCode } from "./vscode"
|
||||
import { normalizeLocale as _normalizeLocale, resolveTemplate as _resolveTemplate } from "./language-utils"
|
||||
|
||||
export type Locale =
|
||||
| "en"
|
||||
| "zh"
|
||||
| "zht"
|
||||
| "ko"
|
||||
| "de"
|
||||
| "es"
|
||||
| "fr"
|
||||
| "da"
|
||||
| "ja"
|
||||
| "pl"
|
||||
| "ru"
|
||||
| "ar"
|
||||
| "no"
|
||||
| "br"
|
||||
| "th"
|
||||
| "bs"
|
||||
|
||||
export const LOCALES: readonly Locale[] = [
|
||||
"en",
|
||||
"zh",
|
||||
"zht",
|
||||
"ko",
|
||||
"de",
|
||||
"es",
|
||||
"fr",
|
||||
"da",
|
||||
"ja",
|
||||
"pl",
|
||||
"ru",
|
||||
"ar",
|
||||
"no",
|
||||
"br",
|
||||
"th",
|
||||
"bs",
|
||||
]
|
||||
export type { Locale } from "./language-utils"
|
||||
export { LOCALES } from "./language-utils"
|
||||
import type { Locale } from "./language-utils"
|
||||
import { LOCALES } from "./language-utils"
|
||||
|
||||
export const LOCALE_LABELS: Record<Locale, string> = {
|
||||
en: "English",
|
||||
@@ -137,33 +106,11 @@ const dicts: Record<Locale, Record<string, string>> = {
|
||||
}
|
||||
|
||||
function normalizeLocale(lang: string): Locale {
|
||||
const lower = lang.toLowerCase()
|
||||
if (lower.startsWith("zh")) {
|
||||
return lower.includes("hant") ? "zht" : "zh"
|
||||
}
|
||||
for (const loc of LOCALES) {
|
||||
if (lower.startsWith(loc)) {
|
||||
return loc
|
||||
}
|
||||
}
|
||||
// Special cases
|
||||
if (lower.startsWith("nb") || lower.startsWith("nn")) {
|
||||
return "no"
|
||||
}
|
||||
if (lower.startsWith("pt")) {
|
||||
return "br"
|
||||
}
|
||||
return "en"
|
||||
return _normalizeLocale(lang)
|
||||
}
|
||||
|
||||
function resolveTemplate(text: string, params?: UiI18nParams) {
|
||||
if (!params) {
|
||||
return text
|
||||
}
|
||||
return text.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_, rawKey) => {
|
||||
const value = params[String(rawKey)]
|
||||
return value === undefined ? "" : String(value)
|
||||
})
|
||||
return _resolveTemplate(text, params as Record<string, string | number | boolean | undefined>)
|
||||
}
|
||||
|
||||
interface LanguageProviderProps {
|
||||
|
||||
Reference in New Issue
Block a user