fix(tests): fix i18n tests

This commit is contained in:
Gabe Kangas
2025-07-13 18:58:55 -07:00
parent 9cf07ae66f
commit 828bddb4f4
3 changed files with 192 additions and 172 deletions
+156 -154
View File
@@ -1,156 +1,158 @@
{ {
"Add your Owncast instance to the Fediverse": "Add your Owncast instance to the Fediverse", "Add your Owncast instance to the Fediverse": "Add your Owncast instance to the Fediverse",
"Admin": { "Admin": {
"EditInstanceDetails": { "EditInstanceDetails": {
"directoryDescription": "Increase your audience by appearing in the <a href=\"https://directory.owncast.online\" target=\"_blank\" rel=\"noreferrer\"><strong>Owncast Directory</strong></a>. This is an external service run by the Owncast project. <a href=\"https://owncast.online/docs/directory/?source=admin\" target=\"_blank\" rel=\"noopener noreferrer\">Learn more</a>.", "directoryDescription": "Increase your audience by appearing in the <a href=\"https://directory.owncast.online\" target=\"_blank\" rel=\"noreferrer\"><strong>Owncast Directory</strong></a>. This is an external service run by the Owncast project. <a href=\"https://owncast.online/docs/directory/?source=admin\" target=\"_blank\" rel=\"noopener noreferrer\">Learn more</a>.",
"offlineMessageDescription": "The offline message is displayed to your page visitors when you're not streaming. Markdown is supported.", "offlineMessageDescription": "The offline message is displayed to your page visitors when you're not streaming. Markdown is supported.",
"serverUrlRequiredForDirectory": "You must set your <strong>Server URL</strong> above to enable the directory." "serverUrlRequiredForDirectory": "You must set your <strong>Server URL</strong> above to enable the directory."
}, },
"emojiPageDescription": "<strong><em>Missing translation Admin.emojiPageDescription: Please report</em></strong>", "emojiPageDescription": "<strong><em>Missing translation Admin.emojiPageDescription: Please report</em></strong>",
"emojiUploadBulkGuide": "<strong><em>Missing translation Admin.emojiUploadBulkGuide: Please report</em></strong>", "emojiUploadBulkGuide": "<strong><em>Missing translation Admin.emojiUploadBulkGuide: Please report</em></strong>",
"emojis": "<strong><em>Missing translation Admin.emojis: Please report</em></strong>", "emojis": "<strong><em>Missing translation Admin.emojis: Please report</em></strong>",
"uploadNewEmoji": "<strong><em>Missing translation Admin.uploadNewEmoji: Please report</em></strong>" "uploadNewEmoji": "<strong><em>Missing translation Admin.uploadNewEmoji: Please report</em></strong>"
}, },
"Banned Users": "Banned Users", "Banned Users": "Banned Users",
"Bring in moderators to help keep your chat in order": "Bring in moderators to help keep your chat in order.", "Bring in moderators to help keep your chat in order": "Bring in moderators to help keep your chat in order.",
"CPU": "CPU", "CPU": "CPU",
"Chat Messages": "Chat Messages", "Chat Messages": "Chat Messages",
"Chat is disabled": "Chat is disabled", "Chat is disabled": "Chat is disabled",
"Chat is offline": "Chat is offline", "Chat is offline": "Chat is offline",
"Chat will be available when the stream is live": "Chat will be available when the stream is live.", "Chat will be available when the stream is live": "Chat will be available when the stream is live.",
"Chat will continue to be disabled until you begin a live stream": "Chat will continue to be disabled until you begin a live stream.", "Chat will continue to be disabled until you begin a live stream": "Chat will continue to be disabled until you begin a live stream.",
"Click and never miss future streams!": "Click and never miss future streams!", "Click and never miss future streams!": "Click and never miss future streams!",
"Common": { "Common": {
"poweredByOwncastVersion": "Powered by <a href='https://owncast.online'>Owncast v{{versionNumber}}</a>" "poweredByOwncastVersion": "Powered by <a href='https://owncast.online'>Owncast v{{versionNumber}}</a>"
}, },
"Common tasks": "Common tasks", "Common tasks": "Common tasks",
"Connected": "Connected", "Connected": "Connected",
"Contribute": "Contribute", "Contribute": "Contribute",
"Current stream": "Current stream", "Current stream": "Current stream",
"Current viewers": "Current viewers", "Current viewers": "Current viewers",
"Disk": "Disk", "Disk": "Disk",
"Documentation": "Documentation", "Documentation": "Documentation",
"Embed your video onto other sites": "Embed your video onto other sites", "Embed your video onto other sites": "Embed your video onto other sites",
"Enable Owncast social features": "Enable Owncast social features", "Enable Owncast social features": "Enable Owncast social features",
"Error": "Error", "Error": "Error",
"FAQ": "FAQ", "FAQ": "FAQ",
"Find an audience on the Owncast Directory": "Find an audience on the Owncast Directory", "Find an audience on the Owncast Directory": "Find an audience on the Owncast Directory",
"Fix your problems": "Fix your problems", "Fix your problems": "Fix your problems",
"Frontend": { "Frontend": {
"chatOffline": "<strong><em>Missing translation Frontend.chatOffline: Please report</em></strong>", "chatOffline": "<strong><em>Missing translation Frontend.chatOffline: Please report</em></strong>",
"componentError": "Error: {{message}}", "componentError": "Error: {{message}}",
"helloWorld": "<strong><em>Missing translation Frontend.helloWorld: Please report</em></strong>", "helloWorld": "<strong><em>Missing translation Frontend.helloWorld: Please report</em></strong>",
"notificationMessage": "<strong><em>Missing translation Frontend.notificationMessage: Please report</em></strong>", "notificationMessage": "<strong><em>Missing translation Frontend.notificationMessage: Please report</em></strong>",
"offlineBasic": "This stream is offline. Check back soon!", "offlineBasic": "This stream is offline. Check back soon!",
"offlineFediverseOnly": "This stream is offline. <span class='follow-link'>Follow</span> {{fediverseAccount}} on the Fediverse to see the next time {{streamer}} goes live.", "offlineFediverseOnly": "This stream is offline. <span class='follow-link'>Follow</span> {{fediverseAccount}} on the Fediverse to see the next time {{streamer}} goes live.",
"offlineNotifyAndFediverse": "This stream is offline. You can <span class='notify-link'>be notified</span> the next time {{streamer}} goes live or <span class='follow-link'>follow</span> {{fediverseAccount}} on the Fediverse.", "offlineNotifyAndFediverse": "This stream is offline. You can <span class='notify-link'>be notified</span> the next time {{streamer}} goes live or <span class='follow-link'>follow</span> {{fediverseAccount}} on the Fediverse.",
"offlineNotifyOnly": "This stream is offline. <span class='notify-link'>Be notified</span> the next time {{streamer}} goes live." "offlineNotifyOnly": "This stream is offline. <span class='notify-link'>Be notified</span> the next time {{streamer}} goes live."
}, },
"Hardware Info": "Hardware Info", "Hardware Info": "Hardware Info",
"Healthy Stream": "Healthy Stream", "Healthy Stream": "Healthy Stream",
"Help configuring my broadcasting software": "Help configuring my broadcasting software", "Help configuring my broadcasting software": "Help configuring my broadcasting software",
"Hidden messages": "Hidden messages", "Hidden messages": "Hidden messages",
"Hide": "Hide", "Hide": "Hide",
"How can we help you?": "How can we help you?", "How can we help you?": "How can we help you?",
"I found a bug": "I found a bug", "I found a bug": "I found a bug",
"I have a general question": "I have a general question", "I have a general question": "I have a general question",
"I want to build add-ons for Owncast": "I want to build add-ons for Owncast", "I want to build add-ons for Owncast": "I want to build add-ons for Owncast",
"I want to configure my owncast instance": "I want to configure my owncast instance", "I want to configure my owncast instance": "I want to configure my owncast instance",
"I want to customize my website": "I want to customize my website", "I want to customize my website": "I want to customize my website",
"I want to embed my stream into another site": "I want to embed my stream into another site", "I want to embed my stream into another site": "I want to embed my stream into another site",
"I want to tweak my video output": "I want to tweak my video output", "I want to tweak my video output": "I want to tweak my video output",
"I want to use an external storage provider": "I want to use an external storage provider", "I want to use an external storage provider": "I want to use an external storage provider",
"IP Bans": "IP Bans", "IP Bans": "IP Bans",
"If you found a bug, then please": "If you found a bug, then please", "If you found a bug, then please": "If you found a bug, then please",
"Inbound Audio Stream": "Inbound Audio Stream", "Inbound Audio Stream": "Inbound Audio Stream",
"Inbound Stream Details": "Inbound Stream Details", "Inbound Stream Details": "Inbound Stream Details",
"Inbound Video Stream": "Inbound Video Stream", "Inbound Video Stream": "Inbound Video Stream",
"Info": "Info", "Info": "Info",
"Input": "Input", "Input": "Input",
"Last 12 hours": "Last 12 hours", "Last 12 hours": "Last 12 hours",
"Last 24 hours": "Last 24 hours", "Last 24 hours": "Last 24 hours",
"Last 3 months": "Last 3 months", "Last 3 months": "Last 3 months",
"Last 30 days": "Last 30 days", "Last 30 days": "Last 30 days",
"Last 6 months": "Last 6 months", "Last 6 months": "Last 6 months",
"Last 7 days": "Last 7 days", "Last 7 days": "Last 7 days",
"Last live ago": "Last live {{timeAgo}} ago", "Last live ago": "Last live {{timeAgo}} ago",
"Learn how to point your existing software to your new server and start streaming your content": "Learn how to point your existing software to your new server and start streaming your content.", "Learn how to point your existing software to your new server and start streaming your content": "Learn how to point your existing software to your new server and start streaming your content.",
"Learn how you can add your Owncast stream to other sites you control": "Learn how you can add your Owncast stream to other sites you control.", "Learn how you can add your Owncast stream to other sites you control": "Learn how you can add your Owncast stream to other sites you control.",
"Learn more": "Learn more", "Learn more": "Learn more",
"Learn more about chat moderation here": "Learn more about chat moderation here.", "Learn more about chat moderation here": "Learn more about chat moderation here.",
"Level": "Level", "Level": "Level",
"Link": "Link", "Link": "Link",
"List yourself in the Owncast Directory and show off your stream": { "List yourself in the Owncast Directory and show off your stream": {
" Enable it in": "List yourself in the Owncast Directory and show off your stream. Enable it in" " Enable it in": "List yourself in the Owncast Directory and show off your stream. Enable it in"
}, },
"Logs": "Logs", "Logs": "Logs",
"Manage the messages from viewers that show up on your stream": "Manage the messages from viewers that show up on your stream.", "Manage the messages from viewers that show up on your stream": "Manage the messages from viewers that show up on your stream.",
"Max viewers last stream": "Max viewers last stream", "Max viewers last stream": "Max viewers last stream",
"Max viewers this stream": "Max viewers this stream", "Max viewers this stream": "Max viewers this stream",
"Memory": "Memory", "Memory": "Memory",
"Message": "Message", "Message": "Message",
"Moderators": "Moderators", "Moderators": "Moderators",
"Most general questions are answered in our": "Most general questions are answered in our", "Most general questions are answered in our": "Most general questions are answered in our",
"News & Updates from Owncast": "News & Updates from Owncast", "News & Updates from Owncast": "News & Updates from Owncast",
"No": "No", "No": "No",
"No hardware details have been collected yet": "No hardware details have been collected yet.", "No hardware details have been collected yet": "No hardware details have been collected yet.",
"No news": "No news.", "No news": "No news.",
"No stream is active": "No stream is active", "No stream is active": "No stream is active",
"No viewer data has been collected yet": "No viewer data has been collected yet.", "No viewer data has been collected yet": "No viewer data has been collected yet.",
"Notify": "Notify", "Notify": "Notify",
"Other": "Other", "Other": "Other",
"Outbound Audio Stream": "Outbound Audio Stream", "Outbound Audio Stream": "Outbound Audio Stream",
"Outbound Stream Details": "Outbound Stream Details", "Outbound Stream Details": "Outbound Stream Details",
"Outbound Video Stream": "Outbound Video Stream", "Outbound Video Stream": "Outbound Video Stream",
"Overridden via command line": "Overridden via command line.", "Overridden via command line": "Overridden via command line.",
"Peak viewer count": "Peak viewer count", "Peak viewer count": "Peak viewer count",
"Playback Health": "Playback Health", "Playback Health": "Playback Health",
"Please wait": "Please wait", "Please wait": "Please wait",
"Read the Docs": "Read the Docs", "Read the Docs": "Read the Docs",
"Show": "Show", "Show": "Show",
"Skip to footer": "Skip to footer", "Skip to footer": "Skip to footer",
"Skip to offline message": "Skip to offline message", "Skip to offline message": "Skip to offline message",
"Skip to page content": "Skip to page content", "Skip to page content": "Skip to page content",
"Skip to player": "Skip to player", "Skip to player": "Skip to player",
"Source": "Source", "Source": "Source",
"Stay updated!": "Stay updated!", "Stay updated!": "Stay updated!",
"Stream health represents": "Stream health represents", "Stream health represents": "Stream health represents",
"Stream started": "Stream started", "Stream started": "Stream started",
"TROUBLESHOOT": "TROUBLESHOOT", "TROUBLESHOOT": "TROUBLESHOOT",
"Testing": { "Testing": {
"itemCount": "<strong><em>Missing translation Testing.itemCount: Please report</em></strong>", "itemCount": "You have {{count}} items",
"messageCount": "<strong><em>Missing translation Testing.messageCount: Please report</em></strong>", "itemCount_one": "You have {{count}} item",
"noPluralKey": "<strong><em>Missing translation Testing.noPluralKey: Please report</em></strong>", "messageCount": "{{sender}} has sent {{count}} messages",
"simpleKey": "<strong><em>Missing translation Testing.simpleKey: Please report</em></strong>" "messageCount_one": "{{sender}} has sent one message",
}, "noPluralKey": "This has no singular key (_one).",
"Time": "Time", "simpleKey": "Simple translation text"
"Timestamp": "Timestamp", },
"Troubleshooting": "Troubleshooting", "Time": "Time",
"Use your broadcasting software": "Use your broadcasting software", "Timestamp": "Timestamp",
"User": "User", "Troubleshooting": "Troubleshooting",
"View": "View", "Use your broadcasting software": "Use your broadcasting software",
"Viewer Info": "Viewer Info", "User": "User",
"Viewers": "Viewers", "View": "View",
"Visible messages": "Visible messages", "Viewer Info": "Viewer Info",
"Visit the": "Visit the", "Viewers": "Viewers",
"Warning": "Warning", "Visible messages": "Visible messages",
"When a stream is active and chat is enabled, connected chat clients will be displayed here": "When a stream is active and chat is enabled, connected chat clients will be displayed here.", "Visit the": "Visit the",
"Yes": "Yes", "Warning": "Warning",
"You can build your own bots, overlays, tools and add-ons with our": "You can build your own bots, overlays, tools and add-ons with our", "When a stream is active and chat is enabled, connected chat clients will be displayed here": "When a stream is active and chat is enabled, connected chat clients will be displayed here.",
"You should start one": "You should start one.", "Yes": "Yes",
"developer APIs": "developer APIs.", "You can build your own bots, overlays, tools and add-ons with our": "You can build your own bots, overlays, tools and add-ons with our",
"discussions": "discussions", "You should start one": "You should start one.",
"documentation": "documentation", "developer APIs": "developer APIs.",
"let us know": "let us know", "discussions": "discussions",
"max viewers": "max viewers", "documentation": "documentation",
"of all known players": { "let us know": "let us know",
" Other player status is unknown": "of all known players. Other player status is unknown." "max viewers": "max viewers",
}, "of all known players": {
"offline": "offline", " Other player status is unknown": "of all known players. Other player status is unknown."
"offline_basic": "This stream is offline. Check back soon!", },
"or exist in our": "or exist in our", "offline": "offline",
"settings": "settings.", "offline_basic": "This stream is offline. Check back soon!",
"to configure additional details about your viewers": "to configure additional details about your viewers.", "or exist in our": "or exist in our",
"to have your instance join the Fediverse, allowing people to follow, share and engage with your live stream": "to have your instance join the Fediverse, allowing people to follow, share and engage with your live stream.", "settings": "settings.",
"used": "used" "to configure additional details about your viewers": "to configure additional details about your viewers.",
"to have your instance join the Fediverse, allowing people to follow, share and engage with your live stream": "to have your instance join the Fediverse, allowing people to follow, share and engage with your live stream.",
"used": "used"
} }
+32 -14
View File
@@ -7,30 +7,48 @@ import { Localization } from '../types/localization';
jest.mock('next-export-i18n', () => ({ jest.mock('next-export-i18n', () => ({
useTranslation: () => ({ useTranslation: () => ({
t: (key: string, vars?: Record<string, any>) => { t: (key: string, vars?: Record<string, any>) => {
// Mock translations for testing // Use the actual keys as in localization.ts and translation.json
const translations: Record<string, string> = { const translations: Record<string, string> = {
hello_world: 'Hello <strong>{{name}}</strong>, welcome to the world!', hello_world: 'Hello <strong>{{name}}</strong>, welcome to the world!',
'Chat is offline': 'Chat is offline', chat_offline: 'Chat is offline',
notification_message: notification_message:
'You can <a href="#">click here</a> to receive notifications when {{streamer}} goes live.', 'You can <a href="#">click here</a> to receive notifications when {{streamer}} goes live.',
simple_key: 'Simple translation text', component_error: 'Error: {{message}}',
offline_basic: 'This stream is offline. Check back soon!',
// Pluralization test keys // Testing keys
item_count_one: 'You have {{count}} item', 'Testing.simpleKey': 'Simple translation text',
item_count: 'You have {{count}} items', // Original key serves as plural form 'Testing.itemCount_one': 'You have {{count}} item',
message_count_one: 'You have {{count}} message from {{sender}}', 'Testing.itemCount': 'You have {{count}} items',
message_count: 'You have {{count}} messages from {{sender}}', // Original key serves as plural form 'Testing.messageCount_one': 'You have {{count}} message from {{sender}}',
'Testing.messageCount': 'You have {{count}} messages from {{sender}}',
// Keys without pluralization variants 'Testing.noPluralKey': 'This key has no plural variants - {{count}} things',
no_plural_key: 'This key has no plural variants - {{count}} things',
}; };
let result = translations[key] || key; let result = translations[key];
// If not found, try to fallback to a snake_case version (for legacy or fallback)
if (!result && key.includes('.')) {
const [ns, k] = key.split('.');
// Try snake_case
const snakeKey =
ns +
'.' +
k
.replace(/([A-Z])/g, '_$1')
.toLowerCase()
.replace(/^_/, '');
result = translations[snakeKey];
}
// If still not found, return the key itself
if (!result) {
result = key;
}
// Simple variable replacement for testing // Simple variable replacement for testing
if (vars) { if (vars) {
Object.keys(vars).forEach(varKey => { Object.keys(vars).forEach(varKey => {
result = result.replace(`{{${varKey}}}`, vars[varKey]); result = result.replace(new RegExp(`{{${varKey}}}`, 'g'), vars[varKey]);
}); });
} }
+4 -4
View File
@@ -118,10 +118,10 @@ export const Localization = {
Testing: { Testing: {
testing: 'testing_string', testing: 'testing_string',
another: 'another_test', another: 'another_test',
simpleKey: 'simple_key', simpleKey: 'Testing.simpleKey',
itemCount: 'item_count', itemCount: 'Testing.itemCount',
messageCount: 'message_count', messageCount: 'Testing.messageCount',
noPluralKey: 'no_plural_key', noPluralKey: 'Testing.noPluralKey',
}, },
} as const; } as const;