feat: update code

This commit is contained in:
pomelo-nwu 2025-12-24 17:12:27 +08:00
parent a8f7bab544
commit a28278e950
7 changed files with 261 additions and 602 deletions

View file

@ -18,10 +18,11 @@ export { getLanguageNameFromLocale };
// State // State
let currentLanguage: SupportedLanguage = 'en'; let currentLanguage: SupportedLanguage = 'en';
let translations: Record<string, string> = {}; let translations: Record<string, string | string[]> = {};
// Cache // Cache
type TranslationDict = Record<string, string>; type TranslationValue = string | string[];
type TranslationDict = Record<string, TranslationValue>;
const translationCache: Record<string, TranslationDict> = {}; const translationCache: Record<string, TranslationDict> = {};
const loadingPromises: Record<string, Promise<TranslationDict>> = {}; const loadingPromises: Record<string, Promise<TranslationDict>> = {};
@ -231,9 +232,25 @@ export function getCurrentLanguage(): SupportedLanguage {
export function t(key: string, params?: Record<string, string>): string { export function t(key: string, params?: Record<string, string>): string {
const translation = translations[key] ?? key; const translation = translations[key] ?? key;
if (Array.isArray(translation)) {
return key;
}
return interpolate(translation, params); return interpolate(translation, params);
} }
/**
* Get a translation that is an array of strings.
* @param key The translation key
* @returns The array of strings, or an empty array if not found or not an array
*/
export function ta(key: string): string[] {
const translation = translations[key];
if (Array.isArray(translation)) {
return translation;
}
return [];
}
export async function initializeI18n( export async function initializeI18n(
lang?: SupportedLanguage | 'auto', lang?: SupportedLanguage | 'auto',
): Promise<void> { ): Promise<void> {

View file

@ -927,192 +927,138 @@ export default {
// ============================================================================ // ============================================================================
'Waiting for user confirmation...': 'Waiting for user confirmation...', 'Waiting for user confirmation...': 'Waiting for user confirmation...',
'(esc to cancel, {{time}})': '(esc to cancel, {{time}})', '(esc to cancel, {{time}})': '(esc to cancel, {{time}})',
"I'm Feeling Lucky": "I'm Feeling Lucky",
'Shipping awesomeness... ': 'Shipping awesomeness... ', // ============================================================================
'Painting the serifs back on...': 'Painting the serifs back on...', // Loading Phrases
'Navigating the slime mold...': 'Navigating the slime mold...', // ============================================================================
'Consulting the digital spirits...': 'Consulting the digital spirits...', WITTY_LOADING_PHRASES: [
'Reticulating splines...': 'Reticulating splines...', "I'm Feeling Lucky",
'Warming up the AI hamsters...': 'Warming up the AI hamsters...', 'Shipping awesomeness... ',
'Asking the magic conch shell...': 'Asking the magic conch shell...', 'Painting the serifs back on...',
'Generating witty retort...': 'Generating witty retort...', 'Navigating the slime mold...',
'Polishing the algorithms...': 'Polishing the algorithms...', 'Consulting the digital spirits...',
"Don't rush perfection (or my code)...": 'Reticulating splines...',
'Warming up the AI hamsters...',
'Asking the magic conch shell...',
'Generating witty retort...',
'Polishing the algorithms...',
"Don't rush perfection (or my code)...", "Don't rush perfection (or my code)...",
'Brewing fresh bytes...': 'Brewing fresh bytes...', 'Brewing fresh bytes...',
'Counting electrons...': 'Counting electrons...', 'Counting electrons...',
'Engaging cognitive processors...': 'Engaging cognitive processors...', 'Engaging cognitive processors...',
'Checking for syntax errors in the universe...':
'Checking for syntax errors in the universe...', 'Checking for syntax errors in the universe...',
'One moment, optimizing humor...': 'One moment, optimizing humor...', 'One moment, optimizing humor...',
'Shuffling punchlines...': 'Shuffling punchlines...', 'Shuffling punchlines...',
'Untangling neural nets...': 'Untangling neural nets...', 'Untangling neural nets...',
'Compiling brilliance...': 'Compiling brilliance...', 'Compiling brilliance...',
'Loading wit.exe...': 'Loading wit.exe...', 'Loading wit.exe...',
'Summoning the cloud of wisdom...': 'Summoning the cloud of wisdom...', 'Summoning the cloud of wisdom...',
'Preparing a witty response...': 'Preparing a witty response...', 'Preparing a witty response...',
"Just a sec, I'm debugging reality...":
"Just a sec, I'm debugging reality...", "Just a sec, I'm debugging reality...",
'Confuzzling the options...': 'Confuzzling the options...', 'Confuzzling the options...',
'Tuning the cosmic frequencies...': 'Tuning the cosmic frequencies...', 'Tuning the cosmic frequencies...',
'Crafting a response worthy of your patience...':
'Crafting a response worthy of your patience...', 'Crafting a response worthy of your patience...',
'Compiling the 1s and 0s...': 'Compiling the 1s and 0s...', 'Compiling the 1s and 0s...',
'Resolving dependencies... and existential crises...':
'Resolving dependencies... and existential crises...', 'Resolving dependencies... and existential crises...',
'Defragmenting memories... both RAM and personal...':
'Defragmenting memories... both RAM and personal...', 'Defragmenting memories... both RAM and personal...',
'Rebooting the humor module...': 'Rebooting the humor module...', 'Rebooting the humor module...',
'Caching the essentials (mostly cat memes)...':
'Caching the essentials (mostly cat memes)...', 'Caching the essentials (mostly cat memes)...',
'Optimizing for ludicrous speed': 'Optimizing for ludicrous speed', 'Optimizing for ludicrous speed',
"Swapping bits... don't tell the bytes...":
"Swapping bits... don't tell the bytes...", "Swapping bits... don't tell the bytes...",
'Garbage collecting... be right back...':
'Garbage collecting... be right back...', 'Garbage collecting... be right back...',
'Assembling the interwebs...': 'Assembling the interwebs...', 'Assembling the interwebs...',
'Converting coffee into code...': 'Converting coffee into code...', 'Converting coffee into code...',
'Updating the syntax for reality...': 'Updating the syntax for reality...', 'Updating the syntax for reality...',
'Rewiring the synapses...': 'Rewiring the synapses...', 'Rewiring the synapses...',
'Looking for a misplaced semicolon...':
'Looking for a misplaced semicolon...', 'Looking for a misplaced semicolon...',
"Greasin' the cogs of the machine...": "Greasin' the cogs of the machine...", "Greasin' the cogs of the machine...",
'Pre-heating the servers...': 'Pre-heating the servers...', 'Pre-heating the servers...',
'Calibrating the flux capacitor...': 'Calibrating the flux capacitor...', 'Calibrating the flux capacitor...',
'Engaging the improbability drive...': 'Engaging the improbability drive...', 'Engaging the improbability drive...',
'Channeling the Force...': 'Channeling the Force...', 'Channeling the Force...',
'Aligning the stars for optimal response...':
'Aligning the stars for optimal response...', 'Aligning the stars for optimal response...',
'So say we all...': 'So say we all...', 'So say we all...',
'Loading the next great idea...': 'Loading the next great idea...', 'Loading the next great idea...',
"Just a moment, I'm in the zone...": "Just a moment, I'm in the zone...", "Just a moment, I'm in the zone...",
'Preparing to dazzle you with brilliance...':
'Preparing to dazzle you with brilliance...', 'Preparing to dazzle you with brilliance...',
"Just a tick, I'm polishing my wit...":
"Just a tick, I'm polishing my wit...", "Just a tick, I'm polishing my wit...",
"Hold tight, I'm crafting a masterpiece...":
"Hold tight, I'm crafting a masterpiece...", "Hold tight, I'm crafting a masterpiece...",
"Just a jiffy, I'm debugging the universe...":
"Just a jiffy, I'm debugging the universe...", "Just a jiffy, I'm debugging the universe...",
"Just a moment, I'm aligning the pixels...":
"Just a moment, I'm aligning the pixels...", "Just a moment, I'm aligning the pixels...",
"Just a sec, I'm optimizing the humor...":
"Just a sec, I'm optimizing the humor...", "Just a sec, I'm optimizing the humor...",
"Just a moment, I'm tuning the algorithms...":
"Just a moment, I'm tuning the algorithms...", "Just a moment, I'm tuning the algorithms...",
'Warp speed engaged...': 'Warp speed engaged...', 'Warp speed engaged...',
'Mining for more Dilithium crystals...':
'Mining for more Dilithium crystals...', 'Mining for more Dilithium crystals...',
"Don't panic...": "Don't panic...", "Don't panic...",
'Following the white rabbit...': 'Following the white rabbit...', 'Following the white rabbit...',
'The truth is in here... somewhere...':
'The truth is in here... somewhere...', 'The truth is in here... somewhere...',
'Blowing on the cartridge...': 'Blowing on the cartridge...', 'Blowing on the cartridge...',
'Loading... Do a barrel roll!': 'Loading... Do a barrel roll!', 'Loading... Do a barrel roll!',
'Waiting for the respawn...': 'Waiting for the respawn...', 'Waiting for the respawn...',
'Finishing the Kessel Run in less than 12 parsecs...':
'Finishing the Kessel Run in less than 12 parsecs...', 'Finishing the Kessel Run in less than 12 parsecs...',
"The cake is not a lie, it's just still loading...":
"The cake is not a lie, it's just still loading...", "The cake is not a lie, it's just still loading...",
'Fiddling with the character creation screen...':
'Fiddling with the character creation screen...', 'Fiddling with the character creation screen...',
"Just a moment, I'm finding the right meme...":
"Just a moment, I'm finding the right meme...", "Just a moment, I'm finding the right meme...",
"Pressing 'A' to continue...": "Pressing 'A' to continue...", "Pressing 'A' to continue...",
'Herding digital cats...': 'Herding digital cats...', 'Herding digital cats...',
'Polishing the pixels...': 'Polishing the pixels...', 'Polishing the pixels...',
'Finding a suitable loading screen pun...':
'Finding a suitable loading screen pun...', 'Finding a suitable loading screen pun...',
'Distracting you with this witty phrase...':
'Distracting you with this witty phrase...', 'Distracting you with this witty phrase...',
'Almost there... probably...': 'Almost there... probably...', 'Almost there... probably...',
'Our hamsters are working as fast as they can...':
'Our hamsters are working as fast as they can...', 'Our hamsters are working as fast as they can...',
'Giving Cloudy a pat on the head...': 'Giving Cloudy a pat on the head...', 'Giving Cloudy a pat on the head...',
'Petting the cat...': 'Petting the cat...', 'Petting the cat...',
'Rickrolling my boss...': 'Rickrolling my boss...', 'Rickrolling my boss...',
'Never gonna give you up, never gonna let you down...':
'Never gonna give you up, never gonna let you down...', 'Never gonna give you up, never gonna let you down...',
'Slapping the bass...': 'Slapping the bass...', 'Slapping the bass...',
'Tasting the snozberries...': 'Tasting the snozberries...', 'Tasting the snozberries...',
"I'm going the distance, I'm going for speed...":
"I'm going the distance, I'm going for speed...", "I'm going the distance, I'm going for speed...",
'Is this the real life? Is this just fantasy?...':
'Is this the real life? Is this just fantasy?...', 'Is this the real life? Is this just fantasy?...',
"I've got a good feeling about this...":
"I've got a good feeling about this...", "I've got a good feeling about this...",
'Poking the bear...': 'Poking the bear...', 'Poking the bear...',
'Doing research on the latest memes...':
'Doing research on the latest memes...', 'Doing research on the latest memes...',
'Figuring out how to make this more witty...':
'Figuring out how to make this more witty...', 'Figuring out how to make this more witty...',
'Hmmm... let me think...': 'Hmmm... let me think...', 'Hmmm... let me think...',
'What do you call a fish with no eyes? A fsh...':
'What do you call a fish with no eyes? A fsh...', 'What do you call a fish with no eyes? A fsh...',
'Why did the computer go to therapy? It had too many bytes...':
'Why did the computer go to therapy? It had too many bytes...', 'Why did the computer go to therapy? It had too many bytes...',
"Why don't programmers like nature? It has too many bugs...":
"Why don't programmers like nature? It has too many bugs...", "Why don't programmers like nature? It has too many bugs...",
'Why do programmers prefer dark mode? Because light attracts bugs...':
'Why do programmers prefer dark mode? Because light attracts bugs...', 'Why do programmers prefer dark mode? Because light attracts bugs...',
'Why did the developer go broke? Because they used up all their cache...':
'Why did the developer go broke? Because they used up all their cache...', 'Why did the developer go broke? Because they used up all their cache...',
"What can you do with a broken pencil? Nothing, it's pointless...":
"What can you do with a broken pencil? Nothing, it's pointless...", "What can you do with a broken pencil? Nothing, it's pointless...",
'Applying percussive maintenance...': 'Applying percussive maintenance...', 'Applying percussive maintenance...',
'Searching for the correct USB orientation...':
'Searching for the correct USB orientation...', 'Searching for the correct USB orientation...',
'Ensuring the magic smoke stays inside the wires...':
'Ensuring the magic smoke stays inside the wires...', 'Ensuring the magic smoke stays inside the wires...',
'Rewriting in Rust for no particular reason...':
'Rewriting in Rust for no particular reason...', 'Rewriting in Rust for no particular reason...',
'Trying to exit Vim...': 'Trying to exit Vim...', 'Trying to exit Vim...',
'Spinning up the hamster wheel...': 'Spinning up the hamster wheel...', 'Spinning up the hamster wheel...',
"That's not a bug, it's an undocumented feature...":
"That's not a bug, it's an undocumented feature...", "That's not a bug, it's an undocumented feature...",
'Engage.': 'Engage.', 'Engage.',
"I'll be back... with an answer.": "I'll be back... with an answer.", "I'll be back... with an answer.",
'My other process is a TARDIS...': 'My other process is a TARDIS...', 'My other process is a TARDIS...',
'Communing with the machine spirit...':
'Communing with the machine spirit...', 'Communing with the machine spirit...',
'Letting the thoughts marinate...': 'Letting the thoughts marinate...', 'Letting the thoughts marinate...',
'Just remembered where I put my keys...':
'Just remembered where I put my keys...', 'Just remembered where I put my keys...',
'Pondering the orb...': 'Pondering the orb...', 'Pondering the orb...',
"I've seen things you people wouldn't believe... like a user who reads loading messages.":
"I've seen things you people wouldn't believe... like a user who reads loading messages.", "I've seen things you people wouldn't believe... like a user who reads loading messages.",
'Initiating thoughtful gaze...': 'Initiating thoughtful gaze...', 'Initiating thoughtful gaze...',
"What's a computer's favorite snack? Microchips.":
"What's a computer's favorite snack? Microchips.", "What's a computer's favorite snack? Microchips.",
"Why do Java developers wear glasses? Because they don't C#.":
"Why do Java developers wear glasses? Because they don't C#.", "Why do Java developers wear glasses? Because they don't C#.",
'Charging the laser... pew pew!': 'Charging the laser... pew pew!', 'Charging the laser... pew pew!',
'Dividing by zero... just kidding!': 'Dividing by zero... just kidding!', 'Dividing by zero... just kidding!',
'Looking for an adult superviso... I mean, processing.':
'Looking for an adult superviso... I mean, processing.', 'Looking for an adult superviso... I mean, processing.',
'Making it go beep boop.': 'Making it go beep boop.', 'Making it go beep boop.',
'Buffering... because even AIs need a moment.':
'Buffering... because even AIs need a moment.', 'Buffering... because even AIs need a moment.',
'Entangling quantum particles for a faster response...':
'Entangling quantum particles for a faster response...', 'Entangling quantum particles for a faster response...',
'Polishing the chrome... on the algorithms.':
'Polishing the chrome... on the algorithms.', 'Polishing the chrome... on the algorithms.',
'Are you not entertained? (Working on it!)':
'Are you not entertained? (Working on it!)', 'Are you not entertained? (Working on it!)',
'Summoning the code gremlins... to help, of course.':
'Summoning the code gremlins... to help, of course.', 'Summoning the code gremlins... to help, of course.',
'Just waiting for the dial-up tone to finish...':
'Just waiting for the dial-up tone to finish...', 'Just waiting for the dial-up tone to finish...',
'Recalibrating the humor-o-meter.': 'Recalibrating the humor-o-meter.', 'Recalibrating the humor-o-meter.',
'My other loading screen is even funnier.':
'My other loading screen is even funnier.', 'My other loading screen is even funnier.',
"Pretty sure there's a cat walking on the keyboard somewhere...":
"Pretty sure there's a cat walking on the keyboard somewhere...", "Pretty sure there's a cat walking on the keyboard somewhere...",
'Enhancing... Enhancing... Still loading.':
'Enhancing... Enhancing... Still loading.', 'Enhancing... Enhancing... Still loading.',
"It's not a bug, it's a feature... of this loading screen.":
"It's not a bug, it's a feature... of this loading screen.", "It's not a bug, it's a feature... of this loading screen.",
'Have you tried turning it off and on again? (The loading screen, not me.)':
'Have you tried turning it off and on again? (The loading screen, not me.)', 'Have you tried turning it off and on again? (The loading screen, not me.)',
'Constructing additional pylons...': 'Constructing additional pylons...', 'Constructing additional pylons...',
],
}; };

View file

@ -945,179 +945,140 @@ export default {
'Waiting for user confirmation...': 'Waiting for user confirmation...':
'Ожидание подтверждения от пользователя...', 'Ожидание подтверждения от пользователя...',
'(esc to cancel, {{time}})': '(esc для отмены, {{time}})', '(esc to cancel, {{time}})': '(esc для отмены, {{time}})',
"I'm Feeling Lucky": 'Мне повезёт!',
'Shipping awesomeness... ': 'Доставляем крутизну... ', // ============================================================================
'Painting the serifs back on...': 'Рисуем засечки на буквах...',
'Navigating the slime mold...': 'Пробираемся через слизевиков..', // ============================================================================
'Consulting the digital spirits...': 'Советуемся с цифровыми духами...', // Loading Phrases
'Reticulating splines...': 'Сглаживание сплайнов...', // ============================================================================
'Warming up the AI hamsters...': 'Разогреваем ИИ-хомячков...', WITTY_LOADING_PHRASES: [
'Asking the magic conch shell...': 'Спрашиваем волшебную ракушку...', 'Мне повезёт!',
'Generating witty retort...': 'Генерируем остроумный ответ...', 'Доставляем крутизну... ',
'Polishing the algorithms...': 'Полируем алгоритмы...', 'Рисуем засечки на буквах...',
"Don't rush perfection (or my code)...": 'Пробираемся через слизевиков..',
'Советуемся с цифровыми духами...',
'Сглаживание сплайнов...',
'Разогреваем ИИ-хомячков...',
'Спрашиваем волшебную ракушку...',
'Генерируем остроумный ответ...',
'Полируем алгоритмы...',
'Не торопите совершенство (или мой код)...', 'Не торопите совершенство (или мой код)...',
'Brewing fresh bytes...': 'Завариваем свежие байты...', 'Завариваем свежие байты...',
'Counting electrons...': 'Пересчитываем электроны...', 'Пересчитываем электроны...',
'Engaging cognitive processors...': 'Задействуем когнитивные процессоры...', 'Задействуем когнитивные процессоры...',
'Checking for syntax errors in the universe...':
'Ищем синтаксические ошибки во вселенной...', 'Ищем синтаксические ошибки во вселенной...',
'One moment, optimizing humor...': 'Секундочку, оптимизируем юмор...', 'Секундочку, оптимизируем юмор...',
'Shuffling punchlines...': 'Перетасовываем панчлайны...', 'Перетасовываем панчлайны...',
'Untangling neural nets...': 'Распутаваем нейросети...', 'Распутаваем нейросети...',
'Compiling brilliance...': 'Компилируем гениальность...', 'Компилируем гениальность...',
'Loading wit.exe...': 'Загружаем yumor.exe...', 'Загружаем yumor.exe...',
'Summoning the cloud of wisdom...': 'Призываем облако мудрости...', 'Призываем облако мудрости...',
'Preparing a witty response...': 'Готовим остроумный ответ...', 'Готовим остроумный ответ...',
"Just a sec, I'm debugging reality...": 'Секунду, идёт отладка реальности...', 'Секунду, идёт отладка реальности...',
'Confuzzling the options...': 'Запутываем варианты...', 'Запутываем варианты...',
'Tuning the cosmic frequencies...': 'Настраиваем космические частоты...', 'Настраиваем космические частоты...',
'Crafting a response worthy of your patience...':
'Создаем ответ, достойный вашего терпения...', 'Создаем ответ, достойный вашего терпения...',
'Compiling the 1s and 0s...': 'Компилируем единички и нолики...', 'Компилируем единички и нолики...',
'Resolving dependencies... and existential crises...':
'Разрешаем зависимости... и экзистенциальные кризисы...', 'Разрешаем зависимости... и экзистенциальные кризисы...',
'Defragmenting memories... both RAM and personal...':
'Дефрагментация памяти... и оперативной, и личной...', 'Дефрагментация памяти... и оперативной, и личной...',
'Rebooting the humor module...': 'Перезагрузка модуля юмора...', 'Перезагрузка модуля юмора...',
'Caching the essentials (mostly cat memes)...':
'Кэшируем самое важное (в основном мемы с котиками)...', 'Кэшируем самое важное (в основном мемы с котиками)...',
'Optimizing for ludicrous speed': 'Оптимизация для безумной скорости', 'Оптимизация для безумной скорости',
"Swapping bits... don't tell the bytes...":
'Меняем биты... только байтам не говорите...', 'Меняем биты... только байтам не говорите...',
'Garbage collecting... be right back...': 'Сборка мусора... скоро вернусь...', 'Сборка мусора... скоро вернусь...',
'Assembling the interwebs...': 'Сборка интернетов...', 'Сборка интернетов...',
'Converting coffee into code...': 'Превращаем кофе в код...', 'Превращаем кофе в код...',
'Updating the syntax for reality...': 'Обновляем синтаксис реальности...', 'Обновляем синтаксис реальности...',
'Rewiring the synapses...': 'Переподключаем синапсы...', 'Переподключаем синапсы...',
'Looking for a misplaced semicolon...': 'Ищем лишнюю точку с запятой...', 'Ищем лишнюю точку с запятой...',
"Greasin' the cogs of the machine...": 'Смазываем шестерёнки машины...', 'Смазываем шестерёнки машины...',
'Pre-heating the servers...': 'Разогреваем серверы...', 'Разогреваем серверы...',
'Calibrating the flux capacitor...': 'Калибруем потоковый накопитель...', 'Калибруем потоковый накопитель...',
'Engaging the improbability drive...': 'Включаем двигатель невероятности...', 'Включаем двигатель невероятности...',
'Channeling the Force...': 'Направляем Силу...', 'Направляем Силу...',
'Aligning the stars for optimal response...':
'Выравниваем звёзды для оптимального ответа...', 'Выравниваем звёзды для оптимального ответа...',
'So say we all...': 'Так скажем мы все...', 'Так скажем мы все...',
'Loading the next great idea...': 'Загрузка следующей великой идеи...', 'Загрузка следующей великой идеи...',
"Just a moment, I'm in the zone...": 'Минутку, я в потоке...', 'Минутку, я в потоке...',
'Preparing to dazzle you with brilliance...':
'Готовлюсь ослепить вас гениальностью...', 'Готовлюсь ослепить вас гениальностью...',
"Just a tick, I'm polishing my wit...": 'Секунду, полирую остроумие...', 'Секунду, полирую остроумие...',
"Hold tight, I'm crafting a masterpiece...": 'Держитесь, создаю шедевр...', 'Держитесь, создаю шедевр...',
"Just a jiffy, I'm debugging the universe...":
'Мигом, отлаживаю вселенную...', 'Мигом, отлаживаю вселенную...',
"Just a moment, I'm aligning the pixels...": 'Момент, выравниваю пиксели...', 'Момент, выравниваю пиксели...',
"Just a sec, I'm optimizing the humor...": 'Секунду, оптимизирую юмор...', 'Секунду, оптимизирую юмор...',
"Just a moment, I'm tuning the algorithms...":
'Момент, настраиваю алгоритмы...', 'Момент, настраиваю алгоритмы...',
'Warp speed engaged...': 'Варп-прыжок активирован...', 'Варп-прыжок активирован...',
'Mining for more Dilithium crystals...': 'Добываем кристаллы дилития...', 'Добываем кристаллы дилития...',
"Don't panic...": 'Без паники...', 'Без паники...',
'Following the white rabbit...': 'Следуем за белым кроликом...', 'Следуем за белым кроликом...',
'The truth is in here... somewhere...': 'Истина где-то здесь... внутри...', 'Истина где-то здесь... внутри...',
'Blowing on the cartridge...': 'Продуваем картридж...', 'Продуваем картридж...',
'Loading... Do a barrel roll!': 'Загрузка... Сделай бочку!', 'Загрузка... Сделай бочку!',
'Waiting for the respawn...': 'Ждем респауна...', 'Ждем респауна...',
'Finishing the Kessel Run in less than 12 parsecs...':
'Делаем Дугу Кесселя менее чем за 12 парсеков...', 'Делаем Дугу Кесселя менее чем за 12 парсеков...',
"The cake is not a lie, it's just still loading...":
'Тортик — не ложь, он просто ещё грузится...', 'Тортик — не ложь, он просто ещё грузится...',
'Fiddling with the character creation screen...':
'Возимся с экраном создания персонажа...', 'Возимся с экраном создания персонажа...',
"Just a moment, I'm finding the right meme...":
'Минутку, ищу подходящий мем...', 'Минутку, ищу подходящий мем...',
"Pressing 'A' to continue...": "Нажимаем 'A' для продолжения...", "Нажимаем 'A' для продолжения...",
'Herding digital cats...': 'Пасём цифровых котов...', 'Пасём цифровых котов...',
'Polishing the pixels...': 'Полируем пиксели...', 'Полируем пиксели...',
'Finding a suitable loading screen pun...':
'Ищем подходящий каламбур для экрана загрузки...', 'Ищем подходящий каламбур для экрана загрузки...',
'Distracting you with this witty phrase...':
'Отвлекаем вас этой остроумной фразой...', 'Отвлекаем вас этой остроумной фразой...',
'Almost there... probably...': 'Почти готово... вроде...', 'Почти готово... вроде...',
'Our hamsters are working as fast as they can...':
'Наши хомячки работают изо всех сил...', 'Наши хомячки работают изо всех сил...',
'Giving Cloudy a pat on the head...': 'Гладим Облачко по голове...', 'Гладим Облачко по голове...',
'Petting the cat...': 'Гладим кота...', 'Гладим кота...',
'Rickrolling my boss...': 'Рикроллим начальника...', 'Рикроллим начальника...',
'Never gonna give you up, never gonna let you down...':
'Never gonna give you up, never gonna let you down...', 'Never gonna give you up, never gonna let you down...',
'Slapping the bass...': 'Лабаем бас-гитару...', 'Лабаем бас-гитару...',
'Tasting the snozberries...': 'Пробуем снузберри на вкус...', 'Пробуем снузберри на вкус...',
"I'm going the distance, I'm going for speed...":
'Иду до конца, иду на скорость...', 'Иду до конца, иду на скорость...',
'Is this the real life? Is this just fantasy?...':
'Is this the real life? Is this just fantasy?...', 'Is this the real life? Is this just fantasy?...',
"I've got a good feeling about this...": 'У меня хорошее предчувствие...', 'У меня хорошее предчувствие...',
'Poking the bear...': 'Дразним медведя... (Не лезь...)', 'Дразним медведя... (Не лезь...)',
'Doing research on the latest memes...': 'Изучаем свежие мемы...', 'Изучаем свежие мемы...',
'Figuring out how to make this more witty...':
'Думаем, как сделать это остроумнее...', 'Думаем, как сделать это остроумнее...',
'Hmmm... let me think...': 'Хмм... дайте подумать...', 'Хмм... дайте подумать...',
'What do you call a fish with no eyes? A fsh...':
'Как называется бумеранг, который не возвращается? Палка...', 'Как называется бумеранг, который не возвращается? Палка...',
'Why did the computer go to therapy? It had too many bytes...':
'Почему компьютер простудился? Потому что оставил окна открытыми...', 'Почему компьютер простудился? Потому что оставил окна открытыми...',
"Why don't programmers like nature? It has too many bugs...":
'Почему программисты не любят гулять на улице? Там среда не настроена...', 'Почему программисты не любят гулять на улице? Там среда не настроена...',
'Why do programmers prefer dark mode? Because light attracts bugs...':
'Почему программисты предпочитают тёмную тему? Потому что в темноте не видно багов...', 'Почему программисты предпочитают тёмную тему? Потому что в темноте не видно багов...',
'Why did the developer go broke? Because they used up all their cache...':
'Почему разработчик разорился? Потому что потратил весь свой кэш...', 'Почему разработчик разорился? Потому что потратил весь свой кэш...',
"What can you do with a broken pencil? Nothing, it's pointless...":
'Что можно делать со сломанным карандашом? Ничего — он тупой...', 'Что можно делать со сломанным карандашом? Ничего — он тупой...',
'Applying percussive maintenance...': 'Провожу настройку методом тыка...', 'Провожу настройку методом тыка...',
'Searching for the correct USB orientation...':
'Ищем, какой стороной вставлять флешку...', 'Ищем, какой стороной вставлять флешку...',
'Ensuring the magic smoke stays inside the wires...':
'Следим, чтобы волшебный дым не вышел из проводов...', 'Следим, чтобы волшебный дым не вышел из проводов...',
'Rewriting in Rust for no particular reason...':
'Переписываем всё на Rust без особой причины...', 'Переписываем всё на Rust без особой причины...',
'Trying to exit Vim...': 'Пытаемся выйти из Vim...', 'Пытаемся выйти из Vim...',
'Spinning up the hamster wheel...': 'Раскручиваем колесо для хомяка...', 'Раскручиваем колесо для хомяка...',
"That's not a bug, it's an undocumented feature...": 'Это не баг, а фича...', 'Это не баг, а фича...',
'Engage.': 'Поехали!', 'Поехали!',
"I'll be back... with an answer.": 'Я вернусь... с ответом.', 'Я вернусь... с ответом.',
'My other process is a TARDIS...': 'Мой другой процесс — это ТАРДИС...', 'Мой другой процесс — это ТАРДИС...',
'Communing with the machine spirit...': 'Общаемся с духом машины...', 'Общаемся с духом машины...',
'Letting the thoughts marinate...': 'Даем мыслям замариноваться...', 'Даем мыслям замариноваться...',
'Just remembered where I put my keys...':
'Только что вспомнил, куда положил ключи...', 'Только что вспомнил, куда положил ключи...',
'Pondering the orb...': 'Размышляю над сферой...', 'Размышляю над сферой...',
"I've seen things you people wouldn't believe... like a user who reads loading messages.":
'Я видел такое, что вам, людям, и не снилось... пользователя, читающего эти сообщения.', 'Я видел такое, что вам, людям, и не снилось... пользователя, читающего эти сообщения.',
'Initiating thoughtful gaze...': 'Инициируем задумчивый взгляд...', 'Инициируем задумчивый взгляд...',
"What's a computer's favorite snack? Microchips.":
'Что сервер заказывает в баре? Пинг-коладу.', 'Что сервер заказывает в баре? Пинг-коладу.',
"Why do Java developers wear glasses? Because they don't C#.":
'Почему Java-разработчики не убираются дома? Они ждут сборщик мусора...', 'Почему Java-разработчики не убираются дома? Они ждут сборщик мусора...',
'Charging the laser... pew pew!': 'Заряжаем лазер... пиу-пиу!', 'Заряжаем лазер... пиу-пиу!',
'Dividing by zero... just kidding!': 'Делим на ноль... шучу!', 'Делим на ноль... шучу!',
'Looking for an adult superviso... I mean, processing.':
'Ищу взрослых для присмот... в смысле, обрабатываю.', 'Ищу взрослых для присмот... в смысле, обрабатываю.',
'Making it go beep boop.': 'Делаем бип-буп.', 'Делаем бип-буп.',
'Buffering... because even AIs need a moment.':
'Буферизация... даже ИИ нужно время подумать.', 'Буферизация... даже ИИ нужно время подумать.',
'Entangling quantum particles for a faster response...':
'Запутываем квантовые частицы для быстрого ответа...', 'Запутываем квантовые частицы для быстрого ответа...',
'Polishing the chrome... on the algorithms.':
'Полируем хром... на алгоритмах.', 'Полируем хром... на алгоритмах.',
'Are you not entertained? (Working on it!)':
'Вы ещё не развлеклись?! Разве вы не за этим сюда пришли?!', 'Вы ещё не развлеклись?! Разве вы не за этим сюда пришли?!',
'Summoning the code gremlins... to help, of course.':
'Призываем гремлинов кода... для помощи, конечно же.', 'Призываем гремлинов кода... для помощи, конечно же.',
'Just waiting for the dial-up tone to finish...':
'Ждем, пока закончится звук dial-up модема...', 'Ждем, пока закончится звук dial-up модема...',
'Recalibrating the humor-o-meter.': 'Перекалибровка юморометра.', 'Перекалибровка юморометра.',
'My other loading screen is even funnier.':
'Мой другой экран загрузки ещё смешнее.', 'Мой другой экран загрузки ещё смешнее.',
"Pretty sure there's a cat walking on the keyboard somewhere...":
'Кажется, где-то по клавиатуре гуляет кот...', 'Кажется, где-то по клавиатуре гуляет кот...',
'Enhancing... Enhancing... Still loading.':
'Улучшаем... Ещё улучшаем... Всё ещё грузится.', 'Улучшаем... Ещё улучшаем... Всё ещё грузится.',
"It's not a bug, it's a feature... of this loading screen.":
'Это не баг, это фича... экрана загрузки.', 'Это не баг, это фича... экрана загрузки.',
'Have you tried turning it off and on again? (The loading screen, not me.)':
'Пробовали выключить и включить снова? (Экран загрузки, не меня!)', 'Пробовали выключить и включить снова? (Экран загрузки, не меня!)',
'Constructing additional pylons...': 'Нужно построить больше пилонов...', 'Нужно построить больше пилонов...',
],
}; };

View file

@ -880,165 +880,39 @@ export default {
// ============================================================================ // ============================================================================
'Waiting for user confirmation...': '等待用户确认...', 'Waiting for user confirmation...': '等待用户确认...',
'(esc to cancel, {{time}})': '(按 esc 取消,{{time}}', '(esc to cancel, {{time}})': '(按 esc 取消,{{time}}',
"I'm Feeling Lucky": '我感觉很幸运', WITTY_LOADING_PHRASES: [
'Shipping awesomeness... ': '正在运送精彩内容... ', // --- 职场搬砖系列 ---
'Painting the serifs back on...': '正在重新绘制衬线...', '正在努力搬砖,请稍候...',
'Navigating the slime mold...': '正在导航粘液霉菌...', '老板在身后,快加载啊!',
'Consulting the digital spirits...': '正在咨询数字精灵...', '头发掉光前,一定能加载完...',
'Reticulating splines...': '正在网格化样条曲线...', '服务器正在深呼吸,准备放大招...',
'Warming up the AI hamsters...': '正在预热 AI 仓鼠...', '正在向服务器投喂咖啡...',
'Asking the magic conch shell...': '正在询问魔法海螺壳...',
'Generating witty retort...': '正在生成机智的反驳...', // --- 大厂黑话系列 ---
'Polishing the algorithms...': '正在打磨算法...', '正在赋能全链路,寻找关键抓手...',
"Don't rush perfection (or my code)...": '不要急于追求完美(或我的代码)...', '正在降本增效,优化加载路径...',
'Brewing fresh bytes...': '正在酿造新鲜字节...', '正在打破部门壁垒,沉淀方法论...',
'Counting electrons...': '正在计算电子...', '正在拥抱变化,迭代核心价值...',
'Engaging cognitive processors...': '正在启动认知处理器...', '正在对齐颗粒度,打磨底层逻辑...',
'Checking for syntax errors in the universe...': '大力出奇迹,正在强行加载...',
'正在检查宇宙中的语法错误...',
'One moment, optimizing humor...': '稍等片刻,正在优化幽默感...', // --- 程序员自嘲系列 ---
'Shuffling punchlines...': '正在洗牌笑点...', '只要我不写代码,代码就没有 Bug...',
'Untangling neural nets...': '正在解开神经网络...', '正在把 Bug 转化为 Feature...',
'Compiling brilliance...': '正在编译智慧...', '只要我不尴尬Bug 就追不上我...',
'Loading wit.exe...': '正在加载 wit.exe...', '正在试图理解去年的自己写了什么...',
'Summoning the cloud of wisdom...': '正在召唤智慧云...', '正在猿力觉醒中,请耐心等待...',
'Preparing a witty response...': '正在准备机智的回复...',
"Just a sec, I'm debugging reality...": '稍等片刻,我正在调试现实...', // --- 合作愉快系列 ---
'Confuzzling the options...': '正在混淆选项...', '正在询问产品经理:这需求是真的吗?',
'Tuning the cosmic frequencies...': '正在调谐宇宙频率...', '正在给产品经理画饼,请稍等...',
'Crafting a response worthy of your patience...':
'正在制作值得您耐心等待的回复...', // --- 温暖治愈系列 ---
'Compiling the 1s and 0s...': '正在编译 1 和 0...', '每一行代码,都在努力让世界变得更好一点点...',
'Resolving dependencies... and existential crises...': '每一个伟大的想法,都值得这份耐心的等待...',
'正在解决依赖关系...和存在主义危机...', '别急,美好的事物总是需要一点时间去酝酿...',
'Defragmenting memories... both RAM and personal...': '愿你的代码永无 Bug愿你的梦想终将成真...',
'正在整理记忆碎片...包括 RAM 和个人记忆...', '哪怕只有 0.1% 的进度,也是在向目标靠近...',
'Rebooting the humor module...': '正在重启幽默模块...', '加载的是字节,承载的是对技术的热爱...',
'Caching the essentials (mostly cat memes)...': ],
'正在缓存必需品(主要是猫咪表情包)...',
'Optimizing for ludicrous speed': '正在优化到荒谬的速度',
"Swapping bits... don't tell the bytes...": '正在交换位...不要告诉字节...',
'Garbage collecting... be right back...': '正在垃圾回收...马上回来...',
'Assembling the interwebs...': '正在组装互联网...',
'Converting coffee into code...': '正在将咖啡转换为代码...',
'Updating the syntax for reality...': '正在更新现实的语法...',
'Rewiring the synapses...': '正在重新连接突触...',
'Looking for a misplaced semicolon...': '正在寻找放错位置的分号...',
"Greasin' the cogs of the machine...": '正在给机器的齿轮上油...',
'Pre-heating the servers...': '正在预热服务器...',
'Calibrating the flux capacitor...': '正在校准通量电容器...',
'Engaging the improbability drive...': '正在启动不可能性驱动器...',
'Channeling the Force...': '正在引导原力...',
'Aligning the stars for optimal response...': '正在对齐星星以获得最佳回复...',
'So say we all...': '我们都说...',
'Loading the next great idea...': '正在加载下一个伟大的想法...',
"Just a moment, I'm in the zone...": '稍等片刻,我正进入状态...',
'Preparing to dazzle you with brilliance...': '正在准备用智慧让您眼花缭乱...',
"Just a tick, I'm polishing my wit...": '稍等片刻,我正在打磨我的智慧...',
"Hold tight, I'm crafting a masterpiece...": '请稍等,我正在制作杰作...',
"Just a jiffy, I'm debugging the universe...": '稍等片刻,我正在调试宇宙...',
"Just a moment, I'm aligning the pixels...": '稍等片刻,我正在对齐像素...',
"Just a sec, I'm optimizing the humor...": '稍等片刻,我正在优化幽默感...',
"Just a moment, I'm tuning the algorithms...": '稍等片刻,我正在调整算法...',
'Warp speed engaged...': '曲速已启动...',
'Mining for more Dilithium crystals...': '正在挖掘更多二锂晶体...',
"Don't panic...": '不要惊慌...',
'Following the white rabbit...': '正在跟随白兔...',
'The truth is in here... somewhere...': '真相在这里...某个地方...',
'Blowing on the cartridge...': '正在吹卡带...',
'Loading... Do a barrel roll!': '正在加载...做个桶滚!',
'Waiting for the respawn...': '等待重生...',
'Finishing the Kessel Run in less than 12 parsecs...':
'正在以不到 12 秒差距完成凯塞尔航线...',
"The cake is not a lie, it's just still loading...":
'蛋糕不是谎言,只是还在加载...',
'Fiddling with the character creation screen...': '正在摆弄角色创建界面...',
"Just a moment, I'm finding the right meme...":
'稍等片刻,我正在寻找合适的表情包...',
"Pressing 'A' to continue...": "按 'A' 继续...",
'Herding digital cats...': '正在放牧数字猫...',
'Polishing the pixels...': '正在打磨像素...',
'Finding a suitable loading screen pun...': '正在寻找合适的加载屏幕双关语...',
'Distracting you with this witty phrase...':
'正在用这个机智的短语分散您的注意力...',
'Almost there... probably...': '快到了...可能...',
'Our hamsters are working as fast as they can...':
'我们的仓鼠正在尽可能快地工作...',
'Giving Cloudy a pat on the head...': '正在拍拍 Cloudy 的头...',
'Petting the cat...': '正在抚摸猫咪...',
'Rickrolling my boss...': '正在 Rickroll 我的老板...',
'Never gonna give you up, never gonna let you down...':
'永远不会放弃你,永远不会让你失望...',
'Slapping the bass...': '正在拍打低音...',
'Tasting the snozberries...': '正在品尝 snozberries...',
"I'm going the distance, I'm going for speed...":
'我要走得更远,我要追求速度...',
'Is this the real life? Is this just fantasy?...':
'这是真实的生活吗?还是只是幻想?...',
"I've got a good feeling about this...": '我对这个感觉很好...',
'Poking the bear...': '正在戳熊...',
'Doing research on the latest memes...': '正在研究最新的表情包...',
'Figuring out how to make this more witty...': '正在想办法让这更有趣...',
'Hmmm... let me think...': '嗯...让我想想...',
'What do you call a fish with no eyes? A fsh...':
'没有眼睛的鱼叫什么?一条鱼...',
'Why did the computer go to therapy? It had too many bytes...':
'为什么电脑去看心理医生?因为它有太多字节...',
"Why don't programmers like nature? It has too many bugs...":
'为什么程序员不喜欢大自然?因为虫子太多了...',
'Why do programmers prefer dark mode? Because light attracts bugs...':
'为什么程序员喜欢暗色模式?因为光会吸引虫子...',
'Why did the developer go broke? Because they used up all their cache...':
'为什么开发者破产了?因为他们用完了所有缓存...',
"What can you do with a broken pencil? Nothing, it's pointless...":
'你能用断了的铅笔做什么?什么都不能,因为它没有笔尖...',
'Applying percussive maintenance...': '正在应用敲击维护...',
'Searching for the correct USB orientation...': '正在寻找正确的 USB 方向...',
'Ensuring the magic smoke stays inside the wires...':
'确保魔法烟雾留在电线内...',
'Rewriting in Rust for no particular reason...':
'正在用 Rust 重写,没有特别的原因...',
'Trying to exit Vim...': '正在尝试退出 Vim...',
'Spinning up the hamster wheel...': '正在启动仓鼠轮...',
"That's not a bug, it's an undocumented feature...":
'这不是一个错误,这是一个未记录的功能...',
'Engage.': '启动。',
"I'll be back... with an answer.": '我会回来的...带着答案。',
'My other process is a TARDIS...': '我的另一个进程是 TARDIS...',
'Communing with the machine spirit...': '正在与机器精神交流...',
'Letting the thoughts marinate...': '让想法慢慢酝酿...',
'Just remembered where I put my keys...': '刚刚想起我把钥匙放在哪里了...',
'Pondering the orb...': '正在思考球体...',
"I've seen things you people wouldn't believe... like a user who reads loading messages.":
'我见过你们不会相信的事情...比如一个阅读加载消息的用户。',
'Initiating thoughtful gaze...': '正在启动深思凝视...',
"What's a computer's favorite snack? Microchips.":
'电脑最喜欢的零食是什么?微芯片。',
"Why do Java developers wear glasses? Because they don't C#.":
'为什么 Java 开发者戴眼镜?因为他们不会 C#。',
'Charging the laser... pew pew!': '正在给激光充电...砰砰!',
'Dividing by zero... just kidding!': '除以零...只是开玩笑!',
'Looking for an adult superviso... I mean, processing.':
'正在寻找成人监督...我是说,处理中。',
'Making it go beep boop.': '让它发出哔哔声。',
'Buffering... because even AIs need a moment.':
'正在缓冲...因为即使是 AI 也需要片刻。',
'Entangling quantum particles for a faster response...':
'正在纠缠量子粒子以获得更快的回复...',
'Polishing the chrome... on the algorithms.': '正在打磨铬...在算法上。',
'Are you not entertained? (Working on it!)': '你不觉得有趣吗?(正在努力!)',
'Summoning the code gremlins... to help, of course.':
'正在召唤代码小精灵...当然是来帮忙的。',
'Just waiting for the dial-up tone to finish...': '只是等待拨号音结束...',
'Recalibrating the humor-o-meter.': '正在重新校准幽默计。',
'My other loading screen is even funnier.': '我的另一个加载屏幕更有趣。',
"Pretty sure there's a cat walking on the keyboard somewhere...":
'很确定有只猫在某个地方键盘上走...',
'Enhancing... Enhancing... Still loading.':
'正在增强...正在增强...仍在加载。',
"It's not a bug, it's a feature... of this loading screen.":
'这不是一个错误,这是一个功能...这个加载屏幕的功能。',
'Have you tried turning it off and on again? (The loading screen, not me.)':
'你试过把它关掉再打开吗?(加载屏幕,不是我。)',
'Constructing additional pylons...': '正在建造额外的能量塔...',
}; };

View file

@ -8,19 +8,22 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act } from '@testing-library/react'; import { renderHook, act } from '@testing-library/react';
import { useLoadingIndicator } from './useLoadingIndicator.js'; import { useLoadingIndicator } from './useLoadingIndicator.js';
import { StreamingState } from '../types.js'; import { StreamingState } from '../types.js';
import { import { PHRASE_CHANGE_INTERVAL_MS } from './usePhraseCycler.js';
WITTY_LOADING_PHRASES, import * as i18n from '../../i18n/index.js';
PHRASE_CHANGE_INTERVAL_MS,
} from './usePhraseCycler.js'; const MOCK_WITTY_PHRASES = ['Phrase 1', 'Phrase 2', 'Phrase 3'];
describe('useLoadingIndicator', () => { describe('useLoadingIndicator', () => {
beforeEach(() => { beforeEach(() => {
vi.useFakeTimers(); vi.useFakeTimers();
vi.spyOn(i18n, 'ta').mockReturnValue(MOCK_WITTY_PHRASES);
vi.spyOn(i18n, 't').mockImplementation((key) => key);
}); });
afterEach(() => { afterEach(() => {
vi.useRealTimers(); // Restore real timers after each test vi.useRealTimers(); // Restore real timers after each test
act(() => vi.runOnlyPendingTimers); act(() => vi.runOnlyPendingTimers);
vi.restoreAllMocks();
}); });
it('should initialize with default values when Idle', () => { it('should initialize with default values when Idle', () => {
@ -28,9 +31,7 @@ describe('useLoadingIndicator', () => {
useLoadingIndicator(StreamingState.Idle), useLoadingIndicator(StreamingState.Idle),
); );
expect(result.current.elapsedTime).toBe(0); expect(result.current.elapsedTime).toBe(0);
expect(WITTY_LOADING_PHRASES).toContain( expect(MOCK_WITTY_PHRASES).toContain(result.current.currentLoadingPhrase);
result.current.currentLoadingPhrase,
);
}); });
it('should reflect values when Responding', async () => { it('should reflect values when Responding', async () => {
@ -40,18 +41,14 @@ describe('useLoadingIndicator', () => {
// Initial state before timers advance // Initial state before timers advance
expect(result.current.elapsedTime).toBe(0); expect(result.current.elapsedTime).toBe(0);
expect(WITTY_LOADING_PHRASES).toContain( expect(MOCK_WITTY_PHRASES).toContain(result.current.currentLoadingPhrase);
result.current.currentLoadingPhrase,
);
await act(async () => { await act(async () => {
await vi.advanceTimersByTimeAsync(PHRASE_CHANGE_INTERVAL_MS + 1); await vi.advanceTimersByTimeAsync(PHRASE_CHANGE_INTERVAL_MS + 1);
}); });
// Phrase should cycle if PHRASE_CHANGE_INTERVAL_MS has passed // Phrase should cycle if PHRASE_CHANGE_INTERVAL_MS has passed
expect(WITTY_LOADING_PHRASES).toContain( expect(MOCK_WITTY_PHRASES).toContain(result.current.currentLoadingPhrase);
result.current.currentLoadingPhrase,
);
}); });
it('should show waiting phrase and retain elapsedTime when WaitingForConfirmation', async () => { it('should show waiting phrase and retain elapsedTime when WaitingForConfirmation', async () => {
@ -104,9 +101,7 @@ describe('useLoadingIndicator', () => {
rerender({ streamingState: StreamingState.Responding }); rerender({ streamingState: StreamingState.Responding });
}); });
expect(result.current.elapsedTime).toBe(0); // Should reset expect(result.current.elapsedTime).toBe(0); // Should reset
expect(WITTY_LOADING_PHRASES).toContain( expect(MOCK_WITTY_PHRASES).toContain(result.current.currentLoadingPhrase);
result.current.currentLoadingPhrase,
);
await act(async () => { await act(async () => {
await vi.advanceTimersByTimeAsync(1000); await vi.advanceTimersByTimeAsync(1000);
@ -130,9 +125,7 @@ describe('useLoadingIndicator', () => {
}); });
expect(result.current.elapsedTime).toBe(0); expect(result.current.elapsedTime).toBe(0);
expect(WITTY_LOADING_PHRASES).toContain( expect(MOCK_WITTY_PHRASES).toContain(result.current.currentLoadingPhrase);
result.current.currentLoadingPhrase,
);
// Timer should not advance // Timer should not advance
await act(async () => { await act(async () => {

View file

@ -8,13 +8,17 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act } from '@testing-library/react'; import { renderHook, act } from '@testing-library/react';
import { import {
usePhraseCycler, usePhraseCycler,
WITTY_LOADING_PHRASES,
PHRASE_CHANGE_INTERVAL_MS, PHRASE_CHANGE_INTERVAL_MS,
} from './usePhraseCycler.js'; } from './usePhraseCycler.js';
import * as i18n from '../../i18n/index.js';
const MOCK_WITTY_PHRASES = ['Phrase 1', 'Phrase 2', 'Phrase 3'];
describe('usePhraseCycler', () => { describe('usePhraseCycler', () => {
beforeEach(() => { beforeEach(() => {
vi.useFakeTimers(); vi.useFakeTimers();
vi.spyOn(i18n, 'ta').mockReturnValue(MOCK_WITTY_PHRASES);
vi.spyOn(i18n, 't').mockImplementation((key) => key);
}); });
afterEach(() => { afterEach(() => {
@ -23,7 +27,7 @@ describe('usePhraseCycler', () => {
it('should initialize with a witty phrase when not active and not waiting', () => { it('should initialize with a witty phrase when not active and not waiting', () => {
const { result } = renderHook(() => usePhraseCycler(false, false)); const { result } = renderHook(() => usePhraseCycler(false, false));
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
}); });
it('should show "Waiting for user confirmation..." when isWaiting is true', () => { it('should show "Waiting for user confirmation..." when isWaiting is true', () => {
@ -47,35 +51,30 @@ describe('usePhraseCycler', () => {
it('should cycle through witty phrases when isActive is true and not waiting', () => { it('should cycle through witty phrases when isActive is true and not waiting', () => {
const { result } = renderHook(() => usePhraseCycler(true, false)); const { result } = renderHook(() => usePhraseCycler(true, false));
// Initial phrase should be one of the witty phrases // Initial phrase should be one of the witty phrases
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
const _initialPhrase = result.current; const _initialPhrase = result.current;
act(() => { act(() => {
vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS); vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS);
}); });
// Phrase should change and be one of the witty phrases // Phrase should change and be one of the witty phrases
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
const _secondPhrase = result.current; const _secondPhrase = result.current;
act(() => { act(() => {
vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS); vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS);
}); });
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
}); });
it('should reset to a witty phrase when isActive becomes true after being false (and not waiting)', () => { it('should reset to a witty phrase when isActive becomes true after being false (and not waiting)', () => {
// Ensure there are at least two phrases for this test to be meaningful.
if (WITTY_LOADING_PHRASES.length < 2) {
return;
}
// Mock Math.random to make the test deterministic. // Mock Math.random to make the test deterministic.
let callCount = 0; let callCount = 0;
vi.spyOn(Math, 'random').mockImplementation(() => { vi.spyOn(Math, 'random').mockImplementation(() => {
// Cycle through 0, 1, 0, 1, ... // Cycle through 0, 1, 0, 1, ...
const val = callCount % 2; const val = callCount % 2;
callCount++; callCount++;
return val / WITTY_LOADING_PHRASES.length; return val / MOCK_WITTY_PHRASES.length;
}); });
const { result, rerender } = renderHook( const { result, rerender } = renderHook(
@ -86,9 +85,9 @@ describe('usePhraseCycler', () => {
// Activate // Activate
rerender({ isActive: true, isWaiting: false }); rerender({ isActive: true, isWaiting: false });
const firstActivePhrase = result.current; const firstActivePhrase = result.current;
expect(WITTY_LOADING_PHRASES).toContain(firstActivePhrase); expect(MOCK_WITTY_PHRASES).toContain(firstActivePhrase);
// With our mock, this should be the first phrase. // With our mock, this should be the first phrase.
expect(firstActivePhrase).toBe(WITTY_LOADING_PHRASES[0]); expect(firstActivePhrase).toBe(MOCK_WITTY_PHRASES[0]);
act(() => { act(() => {
vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS); vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS);
@ -96,18 +95,18 @@ describe('usePhraseCycler', () => {
// Phrase should change to the second phrase. // Phrase should change to the second phrase.
expect(result.current).not.toBe(firstActivePhrase); expect(result.current).not.toBe(firstActivePhrase);
expect(result.current).toBe(WITTY_LOADING_PHRASES[1]); expect(result.current).toBe(MOCK_WITTY_PHRASES[1]);
// Set to inactive - should reset to the default initial phrase // Set to inactive - should reset to the default initial phrase
rerender({ isActive: false, isWaiting: false }); rerender({ isActive: false, isWaiting: false });
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
// Set back to active - should pick a random witty phrase (which our mock controls) // Set back to active - should pick a random witty phrase (which our mock controls)
act(() => { act(() => {
rerender({ isActive: true, isWaiting: false }); rerender({ isActive: true, isWaiting: false });
}); });
// The random mock will now return 0, so it should be the first phrase again. // The random mock will now return 0, so it should be the first phrase again.
expect(result.current).toBe(WITTY_LOADING_PHRASES[0]); expect(result.current).toBe(MOCK_WITTY_PHRASES[0]);
}); });
it('should clear phrase interval on unmount when active', () => { it('should clear phrase interval on unmount when active', () => {
@ -148,7 +147,7 @@ describe('usePhraseCycler', () => {
rerender({ isActive: true, isWaiting: false, customPhrases: undefined }); rerender({ isActive: true, isWaiting: false, customPhrases: undefined });
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
}); });
it('should fall back to witty phrases if custom phrases are an empty array', () => { it('should fall back to witty phrases if custom phrases are an empty array', () => {
@ -164,7 +163,7 @@ describe('usePhraseCycler', () => {
}, },
); );
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
}); });
it('should reset to a witty phrase when transitioning from waiting to active', () => { it('should reset to a witty phrase when transitioning from waiting to active', () => {
@ -174,16 +173,13 @@ describe('usePhraseCycler', () => {
); );
const _initialPhrase = result.current; const _initialPhrase = result.current;
expect(WITTY_LOADING_PHRASES).toContain(_initialPhrase); expect(MOCK_WITTY_PHRASES).toContain(_initialPhrase);
// Cycle to a different phrase (potentially) // Cycle to a different phrase (potentially)
act(() => { act(() => {
vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS); vi.advanceTimersByTime(PHRASE_CHANGE_INTERVAL_MS);
}); });
if (WITTY_LOADING_PHRASES.length > 1) { expect(MOCK_WITTY_PHRASES).toContain(result.current);
// This check is probabilistic with random selection
}
expect(WITTY_LOADING_PHRASES).toContain(result.current);
// Go to waiting state // Go to waiting state
rerender({ isActive: false, isWaiting: true }); rerender({ isActive: false, isWaiting: true });
@ -191,6 +187,6 @@ describe('usePhraseCycler', () => {
// Go back to active cycling - should pick a random witty phrase // Go back to active cycling - should pick a random witty phrase
rerender({ isActive: true, isWaiting: false }); rerender({ isActive: true, isWaiting: false });
expect(WITTY_LOADING_PHRASES).toContain(result.current); expect(MOCK_WITTY_PHRASES).toContain(result.current);
}); });
}); });

View file

@ -5,139 +5,9 @@
*/ */
import { useState, useEffect, useRef, useMemo } from 'react'; import { useState, useEffect, useRef, useMemo } from 'react';
import { t } from '../../i18n/index.js'; import { t, ta } from '../../i18n/index.js';
export const WITTY_LOADING_PHRASES = [ export const WITTY_LOADING_PHRASES: string[] = ["I'm Feeling Lucky"];
"I'm Feeling Lucky",
'Shipping awesomeness... ',
'Painting the serifs back on...',
'Navigating the slime mold...',
'Consulting the digital spirits...',
'Reticulating splines...',
'Warming up the AI hamsters...',
'Asking the magic conch shell...',
'Generating witty retort...',
'Polishing the algorithms...',
"Don't rush perfection (or my code)...",
'Brewing fresh bytes...',
'Counting electrons...',
'Engaging cognitive processors...',
'Checking for syntax errors in the universe...',
'One moment, optimizing humor...',
'Shuffling punchlines...',
'Untangling neural nets...',
'Compiling brilliance...',
'Loading wit.exe...',
'Summoning the cloud of wisdom...',
'Preparing a witty response...',
"Just a sec, I'm debugging reality...",
'Confuzzling the options...',
'Tuning the cosmic frequencies...',
'Crafting a response worthy of your patience...',
'Compiling the 1s and 0s...',
'Resolving dependencies... and existential crises...',
'Defragmenting memories... both RAM and personal...',
'Rebooting the humor module...',
'Caching the essentials (mostly cat memes)...',
'Optimizing for ludicrous speed',
"Swapping bits... don't tell the bytes...",
'Garbage collecting... be right back...',
'Assembling the interwebs...',
'Converting coffee into code...',
'Updating the syntax for reality...',
'Rewiring the synapses...',
'Looking for a misplaced semicolon...',
"Greasin' the cogs of the machine...",
'Pre-heating the servers...',
'Calibrating the flux capacitor...',
'Engaging the improbability drive...',
'Channeling the Force...',
'Aligning the stars for optimal response...',
'So say we all...',
'Loading the next great idea...',
"Just a moment, I'm in the zone...",
'Preparing to dazzle you with brilliance...',
"Just a tick, I'm polishing my wit...",
"Hold tight, I'm crafting a masterpiece...",
"Just a jiffy, I'm debugging the universe...",
"Just a moment, I'm aligning the pixels...",
"Just a sec, I'm optimizing the humor...",
"Just a moment, I'm tuning the algorithms...",
'Warp speed engaged...',
'Mining for more Dilithium crystals...',
"Don't panic...",
'Following the white rabbit...',
'The truth is in here... somewhere...',
'Blowing on the cartridge...',
'Loading... Do a barrel roll!',
'Waiting for the respawn...',
'Finishing the Kessel Run in less than 12 parsecs...',
"The cake is not a lie, it's just still loading...",
'Fiddling with the character creation screen...',
"Just a moment, I'm finding the right meme...",
"Pressing 'A' to continue...",
'Herding digital cats...',
'Polishing the pixels...',
'Finding a suitable loading screen pun...',
'Distracting you with this witty phrase...',
'Almost there... probably...',
'Our hamsters are working as fast as they can...',
'Giving Cloudy a pat on the head...',
'Petting the cat...',
'Rickrolling my boss...',
'Never gonna give you up, never gonna let you down...',
'Slapping the bass...',
'Tasting the snozberries...',
"I'm going the distance, I'm going for speed...",
'Is this the real life? Is this just fantasy?...',
"I've got a good feeling about this...",
'Poking the bear...',
'Doing research on the latest memes...',
'Figuring out how to make this more witty...',
'Hmmm... let me think...',
'What do you call a fish with no eyes? A fsh...',
'Why did the computer go to therapy? It had too many bytes...',
"Why don't programmers like nature? It has too many bugs...",
'Why do programmers prefer dark mode? Because light attracts bugs...',
'Why did the developer go broke? Because they used up all their cache...',
"What can you do with a broken pencil? Nothing, it's pointless...",
'Applying percussive maintenance...',
'Searching for the correct USB orientation...',
'Ensuring the magic smoke stays inside the wires...',
'Rewriting in Rust for no particular reason...',
'Trying to exit Vim...',
'Spinning up the hamster wheel...',
"That's not a bug, it's an undocumented feature...",
'Engage.',
"I'll be back... with an answer.",
'My other process is a TARDIS...',
'Communing with the machine spirit...',
'Letting the thoughts marinate...',
'Just remembered where I put my keys...',
'Pondering the orb...',
"I've seen things you people wouldn't believe... like a user who reads loading messages.",
'Initiating thoughtful gaze...',
"What's a computer's favorite snack? Microchips.",
"Why do Java developers wear glasses? Because they don't C#.",
'Charging the laser... pew pew!',
'Dividing by zero... just kidding!',
'Looking for an adult superviso... I mean, processing.',
'Making it go beep boop.',
'Buffering... because even AIs need a moment.',
'Entangling quantum particles for a faster response...',
'Polishing the chrome... on the algorithms.',
'Are you not entertained? (Working on it!)',
'Summoning the code gremlins... to help, of course.',
'Just waiting for the dial-up tone to finish...',
'Recalibrating the humor-o-meter.',
'My other loading screen is even funnier.',
"Pretty sure there's a cat walking on the keyboard somewhere...",
'Enhancing... Enhancing... Still loading.',
"It's not a bug, it's a feature... of this loading screen.",
'Have you tried turning it off and on again? (The loading screen, not me.)',
'Constructing additional pylons...',
'New line? Thats Ctrl+J.',
];
export const PHRASE_CHANGE_INTERVAL_MS = 15000; export const PHRASE_CHANGE_INTERVAL_MS = 15000;
@ -152,14 +22,16 @@ export const usePhraseCycler = (
isWaiting: boolean, isWaiting: boolean,
customPhrases?: string[], customPhrases?: string[],
) => { ) => {
// Translate all phrases at once if using default phrases // Get phrases from translations if available
const loadingPhrases = useMemo( const loadingPhrases = useMemo(() => {
() => if (customPhrases && customPhrases.length > 0) {
customPhrases && customPhrases.length > 0 return customPhrases;
? customPhrases }
: WITTY_LOADING_PHRASES.map((phrase) => t(phrase)), const translatedPhrases = ta('WITTY_LOADING_PHRASES');
[customPhrases], return translatedPhrases.length > 0
); ? translatedPhrases
: WITTY_LOADING_PHRASES;
}, [customPhrases]);
const [currentLoadingPhrase, setCurrentLoadingPhrase] = useState( const [currentLoadingPhrase, setCurrentLoadingPhrase] = useState(
loadingPhrases[0], loadingPhrases[0],