fix(flags): label Tor country flag

This commit is contained in:
Tommaso Casaburi
2026-06-04 17:18:35 +07:00
parent 54f2cf2b84
commit 0f6d74592e
38 changed files with 108 additions and 52 deletions
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "لوحات الدليل فقط (سريع)",
"boards_loaded": "اللوحات المحملة:",
"link_not_image_or_video_alert": "الرابط ليس صورة أو فيديو.",
"not_a_file": "ليس ملفًا"
"not_a_file": "ليس ملفًا",
"tor_author_flag_label": "هذا المستخدم يستخدم Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "শুধু ডিরেক্টরি বোর্ড (দ্রুত)",
"boards_loaded": "লোড করা বোর্ড:",
"link_not_image_or_video_alert": "লিংকটি ছবি বা ভিডিও নয়।",
"not_a_file": "ফাইল নয়"
"not_a_file": "ফাইল নয়",
"tor_author_flag_label": "এই ব্যবহারকারী Tor ব্যবহার করছেন"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Pouze nástěnky v adresáři (rychlé)",
"boards_loaded": "načtené nástěnky:",
"link_not_image_or_video_alert": "Odkaz není obrázek ani video.",
"not_a_file": "není soubor"
"not_a_file": "není soubor",
"tor_author_flag_label": "Tento uživatel je na síti Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Kun boards i kataloget (hurtigt)",
"boards_loaded": "indlæste boards:",
"link_not_image_or_video_alert": "Linket er ikke et billede eller en video.",
"not_a_file": "ikke en fil"
"not_a_file": "ikke en fil",
"tor_author_flag_label": "Denne bruger er på Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Nur Verzeichnis-Boards (schnell)",
"boards_loaded": "geladene Boards:",
"link_not_image_or_video_alert": "Der Link ist kein Bild oder Video.",
"not_a_file": "keine Datei"
"not_a_file": "keine Datei",
"tor_author_flag_label": "Dieser Benutzer ist auf Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Μόνο boards καταλόγου (γρήγορο)",
"boards_loaded": "φορτωμένοι πίνακες:",
"link_not_image_or_video_alert": "Ο σύνδεσμος δεν είναι εικόνα ή βίντεο.",
"not_a_file": "δεν είναι αρχείο"
"not_a_file": "δεν είναι αρχείο",
"tor_author_flag_label": "Αυτός ο χρήστης είναι στο Tor"
}
+2 -1
View File
@@ -424,5 +424,6 @@
"stats_scope_directory_boards": "Directory boards only (fast)",
"boards_loaded": "boards loaded:",
"link_not_image_or_video_alert": "Link is not an image or video.",
"not_a_file": "not a file"
"not_a_file": "not a file",
"tor_author_flag_label": "This user is on Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Solo boards del directorio (rápido)",
"boards_loaded": "tableros cargados:",
"link_not_image_or_video_alert": "El enlace no es una imagen ni un video.",
"not_a_file": "no es un archivo"
"not_a_file": "no es un archivo",
"tor_author_flag_label": "Este usuario está en Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "فقط بردهای دایرکتوری (سریع)",
"boards_loaded": "بردهای بارگذاری‌شده:",
"link_not_image_or_video_alert": "پیوند تصویر یا ویدیو نیست.",
"not_a_file": "فایل نیست"
"not_a_file": "فایل نیست",
"tor_author_flag_label": "این کاربر روی Tor است"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Vain hakemiston boardit (nopea)",
"boards_loaded": "ladatut boardit:",
"link_not_image_or_video_alert": "Linkki ei ole kuva tai video.",
"not_a_file": "ei ole tiedosto"
"not_a_file": "ei ole tiedosto",
"tor_author_flag_label": "Tämä käyttäjä on Torissa"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Mga board ng directory lang (mabilis)",
"boards_loaded": "mga board na na-load:",
"link_not_image_or_video_alert": "Ang link ay hindi larawan o video.",
"not_a_file": "hindi file"
"not_a_file": "hindi file",
"tor_author_flag_label": "Ang user na ito ay nasa Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Boards de l'annuaire uniquement (rapide)",
"boards_loaded": "boards chargés :",
"link_not_image_or_video_alert": "Le lien n'est pas une image ni une vidéo.",
"not_a_file": "pas un fichier"
"not_a_file": "pas un fichier",
"tor_author_flag_label": "Cet utilisateur est sur Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "רק לוחות מהספרייה (מהיר)",
"boards_loaded": "לוחות שנטענו:",
"link_not_image_or_video_alert": "הקישור אינו תמונה או וידאו.",
"not_a_file": "לא קובץ"
"not_a_file": "לא קובץ",
"tor_author_flag_label": "המשתמש הזה נמצא ב-Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "सिर्फ डायरेक्टरी बोर्ड (तेज़)",
"boards_loaded": "लोड किए गए बोर्ड:",
"link_not_image_or_video_alert": "लिंक कोई छवि या वीडियो नहीं है.",
"not_a_file": "फ़ाइल नहीं है"
"not_a_file": "फ़ाइल नहीं है",
"tor_author_flag_label": "यह उपयोगकर्ता Tor पर है"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Csak a könyvtár boardjai (gyors)",
"boards_loaded": "betöltött boardok:",
"link_not_image_or_video_alert": "A hivatkozás nem kép vagy videó.",
"not_a_file": "nem fájl"
"not_a_file": "nem fájl",
"tor_author_flag_label": "Ez a felhasználó a Tor hálózatán van"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Hanya board direktori (cepat)",
"boards_loaded": "board dimuat:",
"link_not_image_or_video_alert": "Tautan bukan gambar atau video.",
"not_a_file": "bukan file"
"not_a_file": "bukan file",
"tor_author_flag_label": "Pengguna ini ada di Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Solo board della directory (veloce)",
"boards_loaded": "board caricati:",
"link_not_image_or_video_alert": "Il link non è un'immagine né un video.",
"not_a_file": "non è un file"
"not_a_file": "non è un file",
"tor_author_flag_label": "Questo utente è su Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "ディレクトリの掲示板のみ(高速)",
"boards_loaded": "読み込まれた板:",
"link_not_image_or_video_alert": "リンクは画像または動画ではありません。",
"not_a_file": "ファイルではありません"
"not_a_file": "ファイルではありません",
"tor_author_flag_label": "このユーザーはTorを使用しています"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "디렉터리 게시판만 (빠름)",
"boards_loaded": "불러온 게시판:",
"link_not_image_or_video_alert": "링크가 이미지 또는 동영상이 아닙니다.",
"not_a_file": "파일이 아님"
"not_a_file": "파일이 아님",
"tor_author_flag_label": "이 사용자는 Tor를 사용 중입니다"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "फक्त निर्देशिका बोर्ड (जलद)",
"boards_loaded": "लोड केलेले बोर्ड:",
"link_not_image_or_video_alert": "लिंक प्रतिमा किंवा व्हिडिओ नाही.",
"not_a_file": "फाइल नाही"
"not_a_file": "फाइल नाही",
"tor_author_flag_label": "हा वापरकर्ता Tor वर आहे"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Alleen directory-boards (snel)",
"boards_loaded": "geladen boards:",
"link_not_image_or_video_alert": "De link is geen afbeelding of video.",
"not_a_file": "geen bestand"
"not_a_file": "geen bestand",
"tor_author_flag_label": "Deze gebruiker zit op Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Bare katalog-boards (raskt)",
"boards_loaded": "lastede boards:",
"link_not_image_or_video_alert": "Lenken er ikke et bilde eller en video.",
"not_a_file": "ikke en fil"
"not_a_file": "ikke en fil",
"tor_author_flag_label": "Denne brukeren er på Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Tylko boardy z katalogu (szybko)",
"boards_loaded": "załadowane boardy:",
"link_not_image_or_video_alert": "Link nie jest obrazem ani filmem.",
"not_a_file": "to nie plik"
"not_a_file": "to nie plik",
"tor_author_flag_label": "Ten użytkownik jest w sieci Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Somente boards do diretório (rápido)",
"boards_loaded": "boards carregados:",
"link_not_image_or_video_alert": "O link não é uma imagem nem um vídeo.",
"not_a_file": "não é um arquivo"
"not_a_file": "não é um arquivo",
"tor_author_flag_label": "Este usuário está no Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Doar boardurile din director (rapid)",
"boards_loaded": "boarduri încărcate:",
"link_not_image_or_video_alert": "Linkul nu este o imagine sau un videoclip.",
"not_a_file": "nu este fișier"
"not_a_file": "nu este fișier",
"tor_author_flag_label": "Acest utilizator este pe Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Только доски каталога (быстро)",
"boards_loaded": "загруженные доски:",
"link_not_image_or_video_alert": "Ссылка не является изображением или видео.",
"not_a_file": "не файл"
"not_a_file": "не файл",
"tor_author_flag_label": "Этот пользователь находится в Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Vetëm bordet e drejtorisë (shpejt)",
"boards_loaded": "bordet e ngarkuara:",
"link_not_image_or_video_alert": "Lidhja nuk është imazh ose video.",
"not_a_file": "nuk është skedar"
"not_a_file": "nuk është skedar",
"tor_author_flag_label": "Ky përdorues është në Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Endast katalogens boards (snabbt)",
"boards_loaded": "laddade boards:",
"link_not_image_or_video_alert": "Länken är inte en bild eller video.",
"not_a_file": "inte en fil"
"not_a_file": "inte en fil",
"tor_author_flag_label": "Den här användaren är på Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "డైరెక్టరీ బోర్డులు మాత్రమే (వేగంగా)",
"boards_loaded": "లోడ్ చేసిన బోర్డులు:",
"link_not_image_or_video_alert": "లింక్ చిత్రం లేదా వీడియో కాదు.",
"not_a_file": "ఫైల్ కాదు"
"not_a_file": "ఫైల్ కాదు",
"tor_author_flag_label": "ఈ వినియోగదారు Tor లో ఉన్నారు"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "เฉพาะบอร์ดในไดเรกทอรี (เร็ว)",
"boards_loaded": "บอร์ดที่โหลดแล้ว:",
"link_not_image_or_video_alert": "ลิงก์ไม่ใช่รูปภาพหรือวิดีโอ",
"not_a_file": "ไม่ใช่ไฟล์"
"not_a_file": "ไม่ใช่ไฟล์",
"tor_author_flag_label": "ผู้ใช้นี้อยู่บน Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Yalnızca dizin panolari (hızlı)",
"boards_loaded": "yüklenen panolar:",
"link_not_image_or_video_alert": "Bağlantı bir görsel veya video değil.",
"not_a_file": "dosya değil"
"not_a_file": "dosya değil",
"tor_author_flag_label": "Bu kullanıcı Tor'da"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Лише дошки каталогу (швидко)",
"boards_loaded": "завантажені дошки:",
"link_not_image_or_video_alert": "Посилання не є зображенням або відео.",
"not_a_file": "не файл"
"not_a_file": "не файл",
"tor_author_flag_label": "Цей користувач у Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "صرف ڈائریکٹری بورڈز (تیز)",
"boards_loaded": "لوڈ شدہ بورڈز:",
"link_not_image_or_video_alert": "لنک تصویر یا ویڈیو نہیں ہے۔",
"not_a_file": "فائل نہیں ہے"
"not_a_file": "فائل نہیں ہے",
"tor_author_flag_label": "یہ صارف Tor پر ہے"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "Chỉ bảng trong thư mục (nhanh)",
"boards_loaded": "bảng đã tải:",
"link_not_image_or_video_alert": "Liên kết không phải là hình ảnh hoặc video.",
"not_a_file": "không phải tệp"
"not_a_file": "không phải tệp",
"tor_author_flag_label": "Người dùng này đang ở trên Tor"
}
+2 -1
View File
@@ -405,5 +405,6 @@
"stats_scope_directory_boards": "仅目录板块(快速)",
"boards_loaded": "已加载的版块:",
"link_not_image_or_video_alert": "链接不是图片或视频。",
"not_a_file": "不是文件"
"not_a_file": "不是文件",
"tor_author_flag_label": "该用户正在使用 Tor"
}
+21 -15
View File
@@ -1,3 +1,4 @@
import { useTranslation } from 'react-i18next';
import { getCommentFlagFlairs, getAuthorFlagViewModels } from '../lib/comment-flags';
import styles from '../views/post/post.module.css';
@@ -11,6 +12,8 @@ const getBackgroundPosition = (x: number, y: number) => `${x === 0 ? 0 : -x}px $
const transparentPixelSrc = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3C/svg%3E';
const PostAuthorFlags = ({ author, comment, enabled }: PostAuthorFlagsProps) => {
const { t } = useTranslation();
if (!enabled) return null;
const flags = getAuthorFlagViewModels(getCommentFlagFlairs(comment, author));
@@ -18,21 +21,24 @@ const PostAuthorFlags = ({ author, comment, enabled }: PostAuthorFlagsProps) =>
return (
<span className={styles.authorFlags}>
{flags.map((flag) => (
<img
key={flag.key}
alt={flag.label}
className={styles.authorFlag}
src={transparentPixelSrc}
style={{
backgroundImage: `url("${flag.spritePath}")`,
backgroundPosition: getBackgroundPosition(flag.x, flag.y),
width: flag.width,
height: flag.height,
}}
title={flag.label}
/>
))}
{flags.map((flag) => {
const label = flag.labelKey ? t(flag.labelKey, flag.label) : flag.label;
return (
<img
key={flag.key}
alt={label}
className={styles.authorFlag}
src={transparentPixelSrc}
style={{
backgroundImage: `url("${flag.spritePath}")`,
backgroundPosition: getBackgroundPosition(flag.x, flag.y),
width: flag.width,
height: flag.height,
}}
title={label}
/>
);
})}
</span>
);
};
+11 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { getCommentFlagFlairs, getAuthorFlagFlairs, getAuthorFlagViewModels } from '../comment-flags';
import { getCommentFlagFlairs, getAuthorFlagFlairs, getAuthorFlagViewModels, TOR_AUTHOR_FLAG_LABEL_KEY } from '../comment-flags';
describe('comment-flags', () => {
it('parses the pkc-js challenge flair example for country flags', () => {
@@ -25,6 +25,16 @@ describe('comment-flags', () => {
expect(flags[2]).toMatchObject({ label: 'Applejack', x: 80, y: 0 });
});
it('labels the xx country flag as Tor traffic', () => {
const [flag] = getAuthorFlagViewModels([{ text: 'flag:country:xx' }], 1000);
expect(flag).toMatchObject({
key: 'country:xx',
label: 'This user is on Tor',
labelKey: TOR_AUTHOR_FLAG_LABEL_KEY,
});
});
it('normalizes text flag kinds before parsing', () => {
const flags = getAuthorFlagViewModels([{ text: 'FLAG:COUNTRY:DE' }, { text: 'POL:AC' }, { text: 'flag:MLP:AJ' }], 1000);
+6 -1
View File
@@ -2,6 +2,9 @@ import { COUNTRY_FLAG_HEIGHT, COUNTRY_FLAG_WIDTH, getCountryFlagPosition, getCou
import { getBoardFlagDefinition, normalizeBoardFlagKind, type BoardFlagKind } from './board-flags';
const COUNTRY_FLAG_SPRITE_PATH = 'assets/icons/flags-1.png';
const TOR_COUNTRY_CODE = 'xx';
const TOR_AUTHOR_FLAG_LABEL = 'This user is on Tor';
export const TOR_AUTHOR_FLAG_LABEL_KEY = 'tor_author_flag_label';
const FLAG_TEXT_PATTERN = /^flag:([a-z-]+):([a-z0-9-]+)$/i;
const SHORT_FLAG_TEXT_PATTERN = /^(country|geo|pol|political|meme|memeflag|memeflags|pony|mlp):([a-z0-9-]+)$/i;
const COUNTRY_EMOJI_TEXT_PATTERN = /^([a-z]{2}|catalonia|eu|fam|xe|xs|xw|xk|xx)-emoji$/i;
@@ -46,6 +49,7 @@ export interface AuthorFlagViewModel {
type: 'country' | BoardFlagKind;
code: string;
label: string;
labelKey?: string;
spritePath: string;
width: number;
height: number;
@@ -161,7 +165,8 @@ const toCountryFlagViewModel = (codeValue: string): AuthorFlagViewModel | undefi
key: `country:${code}`,
type: 'country',
code,
label: getCountryLabel(code) ?? code.toUpperCase(),
label: code === TOR_COUNTRY_CODE ? TOR_AUTHOR_FLAG_LABEL : (getCountryLabel(code) ?? code.toUpperCase()),
...(code === TOR_COUNTRY_CODE ? { labelKey: TOR_AUTHOR_FLAG_LABEL_KEY } : {}),
spritePath: COUNTRY_FLAG_SPRITE_PATH,
width: COUNTRY_FLAG_WIDTH,
height: COUNTRY_FLAG_HEIGHT,