160 lines
5.6 KiB
TypeScript
160 lines
5.6 KiB
TypeScript
import { t, resolveLocale, setI18nProvider, getI18nProvider } from '../../src/i18n';
|
|
import type { I18nProvider, SupportedLocale } from '../../src/i18n';
|
|
|
|
describe('i18n Core', () => {
|
|
// Save and restore the original provider
|
|
let originalProvider: I18nProvider;
|
|
beforeAll(() => {
|
|
originalProvider = getI18nProvider();
|
|
});
|
|
afterAll(() => {
|
|
setI18nProvider(originalProvider);
|
|
});
|
|
|
|
describe('t() - Translation Function', () => {
|
|
it('should return English translation for a valid key', () => {
|
|
const result = t('en', 'voice.responses.channelLocked');
|
|
expect(result).toBe('Channel Locked! Only you and invited members can join.'); // i18n-ignore
|
|
});
|
|
|
|
it('should return Korean translation for a valid key', () => {
|
|
const result = t('ko', 'voice.responses.channelLocked');
|
|
expect(result).toBe('채널이 잠겼습니다! 초대된 멤버만 참여할 수 있습니다.'); // i18n-ignore
|
|
});
|
|
|
|
it('should fallback to English when key is missing in target locale', () => {
|
|
// en.ts has all keys, so simulate a missing key by testing a deep key
|
|
// All keys should exist in ko, so this test verifies the mechanism
|
|
const result = t('en', 'errorTitles.USER_INPUT');
|
|
expect(result).toBe('Please check your input');
|
|
});
|
|
|
|
it('should return the key itself when not found in any locale', () => {
|
|
const result = t('en', 'nonExistent.key.path');
|
|
expect(result).toBe('nonExistent.key.path');
|
|
});
|
|
|
|
it('should interpolate template variables {{var}}', () => {
|
|
const result = t('en', 'voice.channelReady', { owner: '<@123>' });
|
|
expect(result).toBe('<@123>, your temporary channel is ready! Use the dropdown menu below to manage it.');
|
|
});
|
|
|
|
it('should interpolate Korean template variables', () => {
|
|
const result = t('ko', 'voice.channelReady', { owner: '<@456>' });
|
|
expect(result).toBe('<@456>, 임시 채널이 준비되었습니다! 아래 드롭다운 메뉴로 관리하세요.');
|
|
});
|
|
|
|
it('should replace multiple occurrences of the same variable', () => {
|
|
// Test with a key that has a variable
|
|
const result = t('en', 'voice.defaultRoomName', { username: 'TestUser' });
|
|
expect(result).toBe("TestUser's Room");
|
|
});
|
|
|
|
it('should return Korean default room name', () => {
|
|
const result = t('ko', 'voice.defaultRoomName', { username: '테스터' });
|
|
expect(result).toBe('테스터의 방');
|
|
});
|
|
|
|
it('should handle error message lookups', () => {
|
|
const result = t('en', 'errors.E1001.userMessage');
|
|
expect(result).toBe('The user limit value is invalid.');
|
|
});
|
|
|
|
it('should handle Korean error message lookups', () => {
|
|
const result = t('ko', 'errors.E1001.userMessage');
|
|
expect(result).toBe('사용자 제한 값이 올바르지 않습니다.');
|
|
});
|
|
|
|
it('should handle error resolution lookups', () => {
|
|
const result = t('en', 'errors.E1001.resolution');
|
|
expect(result).toBe('Please enter a number between 0 and 99. (0 = unlimited)');
|
|
});
|
|
});
|
|
|
|
describe('resolveLocale() - Locale Resolution', () => {
|
|
it('should return default "en" when no options given', () => {
|
|
const result = resolveLocale();
|
|
expect(result).toBe('en');
|
|
});
|
|
|
|
it('should prefer userLocale over guildLocale', () => {
|
|
const result = resolveLocale({
|
|
userLocale: 'ko',
|
|
guildLocale: 'en',
|
|
discordLocale: 'en-US',
|
|
});
|
|
expect(result).toBe('ko');
|
|
});
|
|
|
|
it('should use guildLocale when userLocale is null', () => {
|
|
const result = resolveLocale({
|
|
userLocale: null,
|
|
guildLocale: 'ko',
|
|
discordLocale: 'en-US',
|
|
});
|
|
expect(result).toBe('ko');
|
|
});
|
|
|
|
it('should use discordLocale when DB settings are null', () => {
|
|
const result = resolveLocale({
|
|
userLocale: null,
|
|
guildLocale: null,
|
|
discordLocale: 'ko',
|
|
});
|
|
expect(result).toBe('ko');
|
|
});
|
|
|
|
it('should normalize Discord locale "en-US" to "en"', () => {
|
|
const result = resolveLocale({
|
|
userLocale: null,
|
|
guildLocale: null,
|
|
discordLocale: 'en-US',
|
|
});
|
|
expect(result).toBe('en');
|
|
});
|
|
|
|
it('should fallback to "en" for unsupported locale', () => {
|
|
const result = resolveLocale({
|
|
userLocale: 'fr', // Not supported
|
|
guildLocale: null,
|
|
discordLocale: 'ja', // Not supported
|
|
});
|
|
expect(result).toBe('en');
|
|
});
|
|
});
|
|
|
|
describe('I18nProvider Interface - Swappable Provider', () => {
|
|
it('should allow swapping to a custom provider', () => {
|
|
const customProvider: I18nProvider = {
|
|
get: (locale: SupportedLocale, key: string) => {
|
|
if (key === 'test.key') return `Custom: ${locale}`;
|
|
return undefined;
|
|
},
|
|
isSupported: (locale: string): locale is SupportedLocale => locale === 'en',
|
|
getSupportedLocales: () => ['en'] as const,
|
|
};
|
|
|
|
setI18nProvider(customProvider);
|
|
const result = t('en', 'test.key');
|
|
expect(result).toBe('Custom: en');
|
|
|
|
// Restore original
|
|
setI18nProvider(originalProvider);
|
|
});
|
|
|
|
it('should fallback correctly with custom provider', () => {
|
|
const customProvider: I18nProvider = {
|
|
get: () => undefined,
|
|
isSupported: (locale: string): locale is SupportedLocale => locale === 'en',
|
|
getSupportedLocales: () => ['en'] as const,
|
|
};
|
|
|
|
setI18nProvider(customProvider);
|
|
const result = t('en', 'unknown.key');
|
|
expect(result).toBe('unknown.key'); // Should return key as final fallback
|
|
|
|
setI18nProvider(originalProvider);
|
|
});
|
|
});
|
|
});
|