fix: add built-in Thai banner translations
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / API Lint (push) Blocked by required conditions
CI / API Tests (push) Blocked by required conditions
CI / Scanner Lint (push) Blocked by required conditions
CI / Scanner Tests (push) Blocked by required conditions
CI / Banner Lint & Typecheck (push) Blocked by required conditions
CI / Banner Tests (push) Blocked by required conditions
CI / Banner Build (push) Blocked by required conditions
CI / Admin UI Typecheck (push) Blocked by required conditions
CI / Admin UI Tests (push) Blocked by required conditions
CI / Admin UI Build (push) Blocked by required conditions

This commit is contained in:
Kunthawat Greethong
2026-07-01 13:29:38 +07:00
parent 4abb0138e6
commit c89a9f7b3e
2 changed files with 94 additions and 9 deletions

View File

@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
DEFAULT_TRANSLATIONS,
THAI_TRANSLATIONS,
detectLocale,
fetchTranslations,
interpolate,
@@ -46,6 +47,43 @@ describe('i18n', () => {
});
});
describe('THAI_TRANSLATIONS', () => {
it('should have all required keys', () => {
expect(THAI_TRANSLATIONS.title).toBe('เรามีการใช้คุ๊กกี้เพื่อเก็บข้อมูล');
expect(THAI_TRANSLATIONS.acceptAll).toBe('ยอมรับทั้งหมด');
expect(THAI_TRANSLATIONS.rejectAll).toBe('ปฏิเสธทั้งหมด');
expect(THAI_TRANSLATIONS.managePreferences).toBe('ตั้งค่า');
expect(THAI_TRANSLATIONS.savePreferences).toBe('บันทึก');
expect(THAI_TRANSLATIONS.privacyPolicyLink).toBe('นโยบายความเป็นส่วนตัว');
expect(THAI_TRANSLATIONS.closeLabel).toBe('ปิด');
});
it('should have all category translations', () => {
expect(THAI_TRANSLATIONS.categoryNecessary).toBe('จำเป็น');
expect(THAI_TRANSLATIONS.categoryFunctional).toBe('ฟังก์ชั่น');
expect(THAI_TRANSLATIONS.categoryAnalytics).toBe('การวิเคราะห์');
expect(THAI_TRANSLATIONS.categoryMarketing).toBe('การตลาด');
expect(THAI_TRANSLATIONS.categoryPersonalisation).toBe('ส่วนตัว');
});
it('should have category descriptions', () => {
expect(THAI_TRANSLATIONS.categoryNecessaryDesc).toContain('การจดจำตะกร้าสินค้า');
expect(THAI_TRANSLATIONS.categoryFunctionalDesc).toContain('ฟังก์ชั่นพิเศษ');
expect(THAI_TRANSLATIONS.categoryAnalyticsDesc).toContain('ผู้เข้าชมใช้งานเว็บไซต์อย่างไร');
expect(THAI_TRANSLATIONS.categoryMarketingDesc).toContain('Facebook และ Google');
expect(THAI_TRANSLATIONS.categoryPersonalisationDesc).toContain('เก็บข้อมูลส่วนตัว');
});
it('should have cookie count template with placeholder', () => {
expect(THAI_TRANSLATIONS.cookieCount).toContain('{{count}}');
});
it('should preserve legal link placeholders in description', () => {
expect(THAI_TRANSLATIONS.description).toContain('{{privacy_policy}}');
expect(THAI_TRANSLATIONS.description).toContain('{{terms}}');
});
});
describe('normaliseLocale', () => {
it('should extract language code from locale', () => {
expect(normaliseLocale('en-GB')).toBe('en');
@@ -140,16 +178,31 @@ describe('i18n', () => {
expect(t.acceptAll).toBe(DEFAULT_TRANSLATIONS.acceptAll);
});
it('should merge raw public API translations over defaults', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ title: 'เราใช้คุกกี้', acceptAll: 'ยอมรับทั้งหมด' }),
}));
it('should return built-in Thai without calling API', async () => {
const fetchSpy = vi.fn();
vi.stubGlobal('fetch', fetchSpy);
const t = await loadTranslations('https://api.example.com', 'site-123', 'th');
expect(t.title).toBe('เราใช้คุกกี้');
expect(t.title).toBe('เรามีการใช้คุกกี้เพื่อเก็บข้อมูล');
expect(t.acceptAll).toBe('ยอมรับทั้งหมด');
expect(t.rejectAll).toBe('ปฏิเสธทั้งหมด');
// Should NOT have called fetch — Thai is built-in
expect(fetchSpy).not.toHaveBeenCalled();
vi.unstubAllGlobals();
});
it('should merge raw public API translations over defaults', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ title: 'Nous utilisons des cookies', acceptAll: 'Tout accepter' }),
}));
const t = await loadTranslations('https://api.example.com', 'site-123', 'fr');
expect(t.title).toBe('Nous utilisons des cookies');
expect(t.acceptAll).toBe('Tout accepter');
// Missing keys should fall back to English
expect(t.rejectAll).toBe(DEFAULT_TRANSLATIONS.rejectAll);

View File

@@ -51,6 +51,36 @@ export const DEFAULT_TRANSLATIONS: TranslationStrings = {
cookieCount: '{{count}} cookies used on this site',
};
/** Built-in Thai translations — no API call needed. */
export const THAI_TRANSLATIONS: TranslationStrings = {
title: 'เรามีการใช้คุ๊กกี้เพื่อเก็บข้อมูล',
description:
'เว็บไซซ์นี้ใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งาน วิเคราะห์การเข้าชม และแสดงโฆษณาที่เหมาะกับคุณ คุณสามารถเลือกได้ว่าจะอนุญาตคุกกี้ประเภทใด คุกกี้ที่จำเป็นจะถูกเปิดใช้งานเสมอเพื่อให้เว็บไซต์ทำงานได้ [นโยบายความเป็นส่วนตัว]({{privacy_policy}}) [ข้อกำหนดและเงื่อนไข]({{terms}})',
acceptAll: 'ยอมรับทั้งหมด',
rejectAll: 'ปฏิเสธทั้งหมด',
managePreferences: 'ตั้งค่า',
savePreferences: 'บันทึก',
privacyPolicyLink: 'นโยบายความเป็นส่วนตัว',
closeLabel: 'ปิด',
categoryNecessary: 'จำเป็น',
categoryNecessaryDesc: 'คุกกี้ที่จำเป็นสำหรับการทำงานพื้นฐานของเว็บไซต์ เช่น การจดจำตะกร้าสินค้า การเข้าสู่ระบบ และความปลอดภัย — เปิดใช้งานเสมอเพราะเว็บไซต์ไม่สามารถทำงานได้ถ้าไม่มี',
categoryFunctional: 'ฟังก์ชั่น',
categoryFunctionalDesc: 'คุกกี้การตลาดใช้เพื่อให้เว็บไชต์มีฟังก์ชั่นพิเศษ ถ้าปิดการทำงานอาจจะทำให้บางฟังก์ชั่นของเว็บไซต์ใช้งานไม่ได้',
categoryAnalytics: 'การวิเคราะห์',
categoryAnalyticsDesc: 'คุกกี้เหล่านี้ช่วยให้เราเข้าใจว่าผู้เข้าชมใช้งานเว็บไซต์อย่างไร เช่น หน้าไหนที่เข้าบ่อย คลิกที่ไหน - ข้อมูลเหล่านี้ช่วยเราพัฒนาเว็บไซต์ให้ดีขึ้นเรื่อยๆ',
categoryMarketing: 'การตลาด',
categoryMarketingDesc: 'คุกกี้การตลาดใช้ติดตามพฤติกรรมการเข้าชมเว็บไซต์เพื่อแสดงโฆษณาที่เกี่ยวข้องกับความสนใจของคุณบนแพลตฟอร์มอื่นด้วย เช่น Facebook และ Google',
categoryPersonalisation: 'ส่วนตัว',
categoryPersonalisationDesc: 'คุกกี้การตลาดใช้สำหรับเก็บข้อมูลส่วนตัว',
cookieCount: 'มีคุกกี้ {{count}} บนเว็บไซต์นี้',
};
/** Built-in translations that don't require an API call. */
const BUILT_IN_TRANSLATIONS: Record<string, TranslationStrings> = {
en: DEFAULT_TRANSLATIONS,
th: THAI_TRANSLATIONS,
};
/**
* Detect the user's preferred locale.
*
@@ -105,15 +135,17 @@ export async function fetchTranslations(
}
/**
* Load translations: try fetching from API, fall back to defaults.
* Load translations: use built-in if available, otherwise fetch from API.
*/
export async function loadTranslations(
apiBase: string,
siteId: string,
locale: string,
): Promise<TranslationStrings> {
if (locale === 'en') {
return { ...DEFAULT_TRANSLATIONS };
// Built-in translations (en, th, etc.) — no API call needed
const builtIn = BUILT_IN_TRANSLATIONS[locale];
if (builtIn) {
return { ...builtIn };
}
const remote = await fetchTranslations(apiBase, siteId, locale);