Use intl for formats

This commit is contained in:
dangered wolf 2024-04-30 01:27:53 -04:00
parent b1c2b65337
commit 558b271cf5
No known key found for this signature in database
GPG key ID: 41E4D37680ED8B58
3 changed files with 30 additions and 23 deletions

View file

@ -209,7 +209,8 @@ export const handleStatus = async (
status: status, status: status,
thread: thread, thread: thread,
text: newText, text: newText,
flags: flags flags: flags,
targetLanguage: language ?? status.lang ?? 'en'
}); });
headers.push(...instructions.addHeaders); headers.push(...instructions.addHeaders);
if (instructions.authorText) { if (instructions.authorText) {

View file

@ -56,11 +56,16 @@ const generateStatusMedia = (status: APIStatus): string => {
// return `${hh}:${min} - ${yyyy}/${mm}/${dd}`; // return `${hh}:${min} - ${yyyy}/${mm}/${dd}`;
// } // }
const formatDate = (date: Date): string => { const formatDate = (date: Date, language: string): string => {
const yyyy = date.getFullYear(); if (language.startsWith('en')) {
const mm = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed language = 'en-CA'; // Use ISO dates for English to avoid problems with mm/dd vs. dd/mm
const dd = String(date.getDate()).padStart(2, '0'); }
return `${yyyy}/${mm}/${dd}`; const formatter = new Intl.DateTimeFormat(language, {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
return formatter.format(date);
}; };
const htmlifyLinks = (input: string): string => { const htmlifyLinks = (input: string): string => {
@ -108,15 +113,14 @@ function getTranslatedText(status: APITwitterStatus, isQuote = false): string |
const notApplicableComment = '<!-- N/A -->'; const notApplicableComment = '<!-- N/A -->';
// 1100 -> 1.1K, 1100000 -> 1.1M const truncateSocialCount = (count: number, locale = 'en-US') => {
const truncateSocialCount = (count: number): string => { const formatter = new Intl.NumberFormat(locale, {
if (count >= 1000000) { notation: "compact",
return `${(count / 1000000).toFixed(1)}M`; compactDisplay: "short",
} else if (count >= 1000) { maximumFractionDigits: 1
return `${(count / 1000).toFixed(1)}K`; });
} else {
return String(count); return formatter.format(count);
}
}; };
const generateInlineAuthorHeader = ( const generateInlineAuthorHeader = (
@ -166,7 +170,7 @@ const wrapForeignLinks = (url: string) => {
: url; : url;
}; };
const generateStatusFooter = (status: APIStatus, isQuote = false, author: APIUser): string => { const generateStatusFooter = (status: APIStatus, isQuote = false, author: APIUser, language: string): string => {
let description = author.description; let description = author.description;
description = htmlifyLinks(description); description = htmlifyLinks(description);
description = htmlifyHashtags(description); description = htmlifyHashtags(description);
@ -200,10 +204,10 @@ const generateStatusFooter = (status: APIStatus, isQuote = false, author: APIUse
website: author.website website: author.website
? `🔗 <a rel="nofollow" href="${wrapForeignLinks(author.website.url)}">${author.website.display_url}</a>` ? `🔗 <a rel="nofollow" href="${wrapForeignLinks(author.website.url)}">${author.website.display_url}</a>`
: '', : '',
joined: author.joined ? `📆 ${formatDate(new Date(author.joined))}` : '', joined: author.joined ? `📆 ${formatDate(new Date(author.joined), language)}` : '',
following: truncateSocialCount(author.following), following: truncateSocialCount(author.following, language),
followers: truncateSocialCount(author.followers), followers: truncateSocialCount(author.followers, language),
statuses: truncateSocialCount(author.statuses) statuses: truncateSocialCount(author.statuses, language)
}) })
}); });
}; };
@ -270,6 +274,7 @@ const generateCommunityNote = (status: APITwitterStatus): string => {
const generateStatus = ( const generateStatus = (
status: APIStatus, status: APIStatus,
author: APIUser, author: APIUser,
language: string,
isQuote = false, isQuote = false,
authorActionType: AuthorActionType | null authorActionType: AuthorActionType | null
): string => { ): string => {
@ -295,7 +300,7 @@ const generateStatus = (
<!-- Embed poll --> <!-- Embed poll -->
${status.poll ? generatePoll(status.poll, status.lang ?? 'en') : notApplicableComment} ${status.poll ? generatePoll(status.poll, status.lang ?? 'en') : notApplicableComment}
<!-- Embedded quote status --> <!-- Embedded quote status -->
${!isQuote && status.quote ? generateStatus(status.quote, author, true, null) : notApplicableComment} ${!isQuote && status.quote ? generateStatus(status.quote, author, language, true, null) : notApplicableComment}
<br>${!isQuote ? `<a href="${status.url}">${i18next.t('ivViewOriginal')}</a>` : notApplicableComment} <br>${!isQuote ? `<a href="${status.url}">${i18next.t('ivViewOriginal')}</a>` : notApplicableComment}
`.format({ `.format({
quoteHeader: isQuote quoteHeader: isQuote
@ -387,10 +392,10 @@ export const renderInstantView = (properties: RenderProperties): ResponseInstruc
previousThreadPieceAuthor = status.author?.id; previousThreadPieceAuthor = status.author?.id;
return generateStatus(status, status.author ?? thread?.author, false, authorAction); return generateStatus(status, status.author ?? thread?.author, properties?.targetLanguage ?? 'en', false, authorAction,);
}) })
.join('')} .join('')}
${generateStatusFooter(status, false, thread?.author ?? status.author)} ${generateStatusFooter(status, false, thread?.author ?? status.author, properties?.targetLanguage ?? 'en')}
<br>${`<a href="${status.url}">${i18next.t('ivViewOriginal')}</a>`} <br>${`<a href="${status.url}">${i18next.t('ivViewOriginal')}</a>`}
</article>`; </article>`;

View file

@ -38,6 +38,7 @@ interface RenderProperties {
userAgent?: string; userAgent?: string;
text?: string; text?: string;
flags?: InputFlags; flags?: InputFlags;
targetLanguage?: string;
} }
interface TweetAPIResponse { interface TweetAPIResponse {