feat: add i18n

This commit is contained in:
kawamataryo 2024-12-07 22:40:33 +09:00
parent 3c03d99a16
commit fc91c4d3a2
21 changed files with 1882 additions and 93 deletions

View File

@ -1,5 +1,16 @@
import type { Preview } from "@storybook/react";
import "../src/style.content.css";
import messages from "../locales/en/messages.json";
const getMessage = (key: string, placeholders: string[]) => {
return messages[key].message
};
window.chrome = {
i18n: {
getMessage: getMessage,
} as typeof chrome.i18n,
} as typeof chrome;
const preview: Preview = {
parameters: {

244
locales/en/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "Learn more",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "Auth Factor Token",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "A 2FA token has been sent to your email",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "Find Bluesky Users",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "Finding Bluesky Users",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "Password",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "Handle or Email",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "We recommend using the [App Password](https://bsky.app/settings/app-passwords).",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "Error: Please enter your password.",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "Error: Please enter your handle or email.",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "Error: Please enter your handle or email and password.",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "Error: Please enter your auth factor token.",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "Error: Invalid handle or password.",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "Error: Invalid page. please open the target page.",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "Error: Something went wrong. Please reload the web page and try again.",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "Error: Invalid page. please open the followers or following view.",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "Scanning $service$ users to find bsky users...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "Found <span class='text-4xl'>$users$</span> users",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "Stop Scanning and View Results",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "Resume Scanning",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "View Detected Users",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "Proceed with Execution?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "User detection is not perfect and may include false positives.",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "Proceed with Execution?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "Importing a list will create a new list and add all detected users to it. This feature is experimental and may not work as expected.",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "Cancel",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "OK",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "Processing...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "Followed $count$ users🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "Blocked $count$ users🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success_view_list": {
"message": "View Imported List",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_success": {
"message": "List imported successfully🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_error": {
"message": "Failed to import list: $error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "Source",
"description": "Text for the source"
},
"detected": {
"message": "Detected",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "User detection is not perfect and may include false positives.",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "This tool is developed by an individual. Your [support](https://ko-fi.com/kawamataryo) helps maintain and improve it. Thank you!",
"description": "Text for the donate message"
},
"follow_all": {
"message": "Follow All",
"description": "Text for the follow all button"
},
"block_all": {
"message": "Block All",
"description": "Text for the block all button"
},
"import_list": {
"message": "Import List",
"description": "Text for the import list button"
},
"followed_users": {
"message": "Followed Users",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "Same Handle Name",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "Same Display Name",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "Included Handle in Description",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "Blocked Users",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "Detected Users",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "Follow on Bluesky",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "Following on Bluesky",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "Unfollow on Bluesky",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "Block on Bluesky",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "Blocking on Bluesky",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "Unblock on Bluesky",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "Loading...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "Search Results",
"description": "Text for the re search modal title"
}
}

244
locales/es/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "Más información",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "Token de autenticación",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "Se ha enviado un token 2FA a tu correo electrónico",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "Buscar usuarios de Bluesky",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "Buscando usuarios de Bluesky",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "Contraseña",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "Usuario o correo electrónico",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "Recomendamos usar la [Contraseña de aplicación](https://bsky.app/settings/app-passwords).",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "Error: Por favor, introduce tu contraseña.",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "Error: Por favor, introduce tu usuario o correo electrónico.",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "Error: Por favor, introduce tu usuario o correo electrónico y contraseña.",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "Error: Por favor, introduce tu token de autenticación.",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "Error: Usuario o contraseña inválidos.",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "Error: Página inválida. Por favor, abre la página objetivo.",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "Error: Algo salió mal. Por favor, recarga la página web e inténtalo de nuevo.",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "Error: Página inválida. Por favor, abre la vista de seguidores o seguidos.",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "Escaneando usuarios de $service$ para encontrar usuarios de Bluesky...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "Se han detectado <span class='text-4xl'>$users$</span> usuarios",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "Detener escaneo y ver resultados",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "Reanudar escaneo",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "Ver usuarios detectados",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "¿Proceder con la ejecución?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "La detección de usuarios no es perfecta y puede incluir falsos positivos.",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "¿Proceder con la ejecución?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "Importar una lista creará una nueva lista y añadirá todos los usuarios detectados. Esta función es experimental y puede no funcionar como se espera.",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "Cancelar",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "Aceptar",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "Procesando...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "Siguiendo a $count$ usuarios 🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "Bloqueados $count$ usuarios 🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success_view_list": {
"message": "Ver lista importada",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_success": {
"message": "Lista importada exitosamente 🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_error": {
"message": "Error al importar lista: $error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "Origen",
"description": "Text for the source"
},
"detected": {
"message": "Detectado",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "La detección de usuarios no es perfecta y puede incluir falsos positivos.",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "Esta herramienta está desarrollada por un individuo. Tu [apoyo](https://ko-fi.com/kawamataryo) ayuda a mantenerla y mejorarla. ¡Gracias!",
"description": "Text for the donate message"
},
"follow_all": {
"message": "Seguir a todos",
"description": "Text for the follow all button"
},
"block_all": {
"message": "Bloquear a todos",
"description": "Text for the block all button"
},
"import_list": {
"message": "Importar lista",
"description": "Text for the import list button"
},
"followed_users": {
"message": "Usuarios seguidos",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "Mismo nombre de usuario",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "Mismo nombre mostrado",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "Usuario incluido en la descripción",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "Usuarios bloqueados",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "Usuarios detectados",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "Seguir en Bluesky",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "Siguiendo en Bluesky",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "Dejar de seguir en Bluesky",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "Bloquear en Bluesky",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "Bloqueando en Bluesky",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "Desbloquear en Bluesky",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "Cargando...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "Resultados de búsqueda",
"description": "Text for the re search modal title"
}
}

244
locales/fr/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "En savoir plus",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "Jeton d'authentification",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "Un jeton 2FA a été envoyé à votre e-mail",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "Trouver des utilisateurs Bluesky",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "Recherche d'utilisateurs Bluesky",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "Mot de passe",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "Identifiant ou e-mail",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "Nous recommandons d'utiliser le [Mot de passe d'application](https://bsky.app/settings/app-passwords).",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "Erreur : Veuillez saisir votre mot de passe.",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "Erreur : Veuillez saisir votre identifiant ou e-mail.",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "Erreur : Veuillez saisir votre identifiant ou e-mail et mot de passe.",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "Erreur : Veuillez saisir votre jeton d'authentification.",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "Erreur : Identifiant ou mot de passe invalide.",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "Erreur : Page invalide. Veuillez ouvrir la page cible.",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "Erreur : Une erreur s'est produite. Veuillez recharger la page web et réessayer.",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "Erreur : Page invalide. Veuillez ouvrir la vue des abonnés ou des abonnements.",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "Analyse des utilisateurs $service$ pour trouver des utilisateurs Bluesky...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "<span class='text-4xl'>$users$</span> utilisateurs détectés",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "Arrêter l'analyse et voir les résultats",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "Reprendre l'analyse",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "Voir les utilisateurs détectés",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "Procéder à l'exécution ?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "La détection des utilisateurs n'est pas parfaite et peut inclure des faux positifs.",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "Procéder à l'exécution ?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "L'importation d'une liste créera une nouvelle liste et y ajoutera tous les utilisateurs détectés. Cette fonctionnalité est expérimentale et peut ne pas fonctionner comme prévu.",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "Annuler",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "OK",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "Traitement en cours...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "$count$ utilisateurs suivis 🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "$count$ utilisateurs bloqués 🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success_view_list": {
"message": "Voir la liste importée",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_success": {
"message": "Liste importée avec succès 🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_error": {
"message": "Échec de l'importation de la liste : $error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "Source",
"description": "Text for the source"
},
"detected": {
"message": "Détecté",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "La détection des utilisateurs n'est pas parfaite et peut inclure des faux positifs.",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "Cet outil est développé par un individu. Votre [soutien](https://ko-fi.com/kawamataryo) aide à le maintenir et à l'améliorer. Merci !",
"description": "Text for the donate message"
},
"follow_all": {
"message": "Suivre tous",
"description": "Text for the follow all button"
},
"block_all": {
"message": "Bloquer tous",
"description": "Text for the block all button"
},
"import_list": {
"message": "Importer la liste",
"description": "Text for the import list button"
},
"followed_users": {
"message": "Utilisateurs suivis",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "Même identifiant",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "Même nom d'affichage",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "Identifiant inclus dans la description",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "Utilisateurs bloqués",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "Utilisateurs détectés",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "Suivre sur Bluesky",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "Suivi sur Bluesky",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "Ne plus suivre sur Bluesky",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "Bloquer sur Bluesky",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "Bloqué sur Bluesky",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "Débloquer sur Bluesky",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "Chargement...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "Résultats de recherche",
"description": "Text for the re search modal title"
}
}

244
locales/it/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "Scopri di più",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "Token di autenticazione",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "Un token 2FA è stato inviato alla tua email",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "Trova utenti Bluesky",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "Ricerca utenti Bluesky",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "Password",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "Nome utente o Email",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "Si consiglia di utilizzare la [Password dell'app](https://bsky.app/settings/app-passwords).",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "Errore: Inserisci la tua password.",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "Errore: Inserisci il tuo nome utente o email.",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "Errore: Inserisci il tuo nome utente o email e password.",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "Errore: Inserisci il tuo token di autenticazione.",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "Errore: Nome utente o password non validi.",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "Errore: Pagina non valida. Apri la pagina di destinazione.",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "Errore: Qualcosa è andato storto. Ricarica la pagina web e riprova.",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "Errore: Pagina non valida. Apri la vista follower o seguiti.",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "Scansione degli utenti $service$ per trovare utenti Bluesky...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "Trovati <span class='text-4xl'>$users$</span> utenti",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "Interrompi scansione e visualizza risultati",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "Riprendi scansione",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "Visualizza utenti rilevati",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "Procedere con l'esecuzione?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "Il rilevamento degli utenti non è perfetto e potrebbe includere falsi positivi.",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "Procedere con l'esecuzione?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "L'importazione di una lista creerà una nuova lista e aggiungerà tutti gli utenti rilevati. Questa funzione è sperimentale e potrebbe non funzionare come previsto.",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "Annulla",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "OK",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "Elaborazione in corso...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "Seguiti $count$ utenti 🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "Bloccati $count$ utenti 🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success_view_list": {
"message": "Visualizza lista importata",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_success": {
"message": "Lista importata con successo 🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_error": {
"message": "Impossibile importare la lista: $error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "Fonte",
"description": "Text for the source"
},
"detected": {
"message": "Rilevato",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "Il rilevamento degli utenti non è perfetto e potrebbe includere falsi positivi.",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "Questo strumento è sviluppato da un individuo. Il tuo [supporto](https://ko-fi.com/kawamataryo) aiuta a mantenerlo e migliorarlo. Grazie!",
"description": "Text for the donate message"
},
"follow_all": {
"message": "Segui tutti",
"description": "Text for the follow all button"
},
"block_all": {
"message": "Blocca tutti",
"description": "Text for the block all button"
},
"import_list": {
"message": "Importa lista",
"description": "Text for the import list button"
},
"followed_users": {
"message": "Utenti seguiti",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "Stesso nome utente",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "Stesso nome visualizzato",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "Nome utente incluso nella descrizione",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "Utenti bloccati",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "Utenti rilevati",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "Segui su Bluesky",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "Segui già su Bluesky",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "Smetti di seguire su Bluesky",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "Blocca su Bluesky",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "Bloccato su Bluesky",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "Sblocca su Bluesky",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "Caricamento...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "Risultati della ricerca",
"description": "Text for the re search modal title"
}
}

244
locales/ja/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "詳細を見る",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "2要素認証トークン",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "2要素認証トークンがメールに送信されました",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "Blueskyユーザーを探す",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "検索中",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "パスワード",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "ハンドル名またはメールアドレス",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "[アプリパスワード](https://bsky.app/settings/app-passwords)の使用をお勧めします",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "エラー: パスワードを入力してください。",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "エラー: ハンドルまたはメールを入力してください。",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "エラー: ハンドルまたはメールとパスワードを入力してください。",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "エラー: 2要素認証トークンを入力してください。",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "エラー: ハンドル名またはパスワードが無効です。",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "エラー: 無効なページです。対象のページを開いてください。",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "エラー: 問題が発生しました。ウェブページをリロードして再試行してください。",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "エラー: 無効なページです。フォロワー/フォロー中一覧を開いてください。",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "$service$のからBlueskyのユーザーを検索中...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "<span class='text-4xl'>$users$</span> 人を検出",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "検索を停止して結果を表示",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "検索を再開",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "検出したユーザーを表示",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "実行しますか?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "ユーザー検出は完璧ではありません。誤検出が含まれる可能性があります。",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "実行しますか?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "リストのインポートは実験的な機能です。また、ユーザー検出は完璧ではありません。誤検出が含まれる可能性があります。",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "キャンセル",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "OK",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "処理中...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "$count$ユーザーをフォローしました🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "$count$ユーザーをブロックしました🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success": {
"message": "リストをインポートしました🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_success_view_list": {
"message": "リストを表示",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_error": {
"message": "リストのインポートに失敗しました: $error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "検出元",
"description": "Text for the source"
},
"detected": {
"message": "結果",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "ユーザー検出は完璧ではありません。誤検出が含まれる可能性があります。",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "このツールは個人で開発しています。[支援](https://ko-fi.com/kawamataryo)いただけると、今後の開発の励みになります。",
"description": "Text for the donate message"
},
"follow_all": {
"message": "全てをフォロー",
"description": "Text for the follow all button"
},
"block_all": {
"message": "全てをブロック",
"description": "Text for the block all button"
},
"import_list": {
"message": "リストをインポート",
"description": "Text for the import list button"
},
"followed_users": {
"message": "フォロー中のユーザー",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "同じハンドル名",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "同じ表示名",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "ハンドル名を概要に含む",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "ブロック中のユーザー",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "検出したユーザー数",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "Blueskyでフォロー",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "Blueskyでフォロー中",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "Blueskyでフォロー解除",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "Blueskyでブロック",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "Blueskyでブロック中",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "Blueskyでブロック解除",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "読み込み中...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "再検索",
"description": "Text for the re search modal title"
}
}

244
locales/pt/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "Saiba mais",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "Token de autenticação",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "Um token 2FA foi enviado para seu e-mail",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "Encontrar usuários do Bluesky",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "Procurando usuários do Bluesky",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "Senha",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "Nome de usuário ou E-mail",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "Recomendamos usar a [Senha do aplicativo](https://bsky.app/settings/app-passwords).",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "Erro: Por favor, insira sua senha.",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "Erro: Por favor, insira seu nome de usuário ou e-mail.",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "Erro: Por favor, insira seu nome de usuário ou e-mail e senha.",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "Erro: Por favor, insira seu token de autenticação.",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "Erro: Nome de usuário ou senha inválidos.",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "Erro: Página inválida. Por favor, abra a página de destino.",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "Erro: Algo deu errado. Por favor, recarregue a página e tente novamente.",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "Erro: Página inválida. Por favor, abra a visualização de seguidores ou seguindo.",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "Escaneando usuários do $service$ para encontrar usuários do Bluesky...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "Encontramos <span class='text-4xl'>$users$</span> usuários",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "Parar escaneamento e ver resultados",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "Retomar escaneamento",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "Ver usuários detectados",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "Prosseguir com a execução?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "A detecção de usuários não é perfeita e pode incluir falsos positivos.",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "Prosseguir com a execução?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "Importar uma lista criará uma nova lista e adicionará todos os usuários detectados. Este recurso é experimental e pode não funcionar como esperado.",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "Cancelar",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "OK",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "Processando...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "Seguindo $count$ usuários 🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "Bloqueados $count$ usuários 🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success_view_list": {
"message": "Ver lista importada",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_success": {
"message": "Lista importada com sucesso 🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_error": {
"message": "Falha ao importar lista: $error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "Fonte",
"description": "Text for the source"
},
"detected": {
"message": "Detectado",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "A detecção de usuários não é perfeita e pode incluir falsos positivos.",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "Esta ferramenta é desenvolvida por um indivíduo. Seu [apoio](https://ko-fi.com/kawamataryo) ajuda a mantê-la e melhorá-la. Obrigado!",
"description": "Text for the donate message"
},
"follow_all": {
"message": "Seguir todos",
"description": "Text for the follow all button"
},
"block_all": {
"message": "Bloquear todos",
"description": "Text for the block all button"
},
"import_list": {
"message": "Importar lista",
"description": "Text for the import list button"
},
"followed_users": {
"message": "Usuários seguidos",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "Mesmo nome de usuário",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "Mesmo nome de exibição",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "Nome de usuário incluído na descrição",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "Usuários bloqueados",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "Usuários detectados",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "Seguir no Bluesky",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "Seguindo no Bluesky",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "Deixar de seguir no Bluesky",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "Bloquear no Bluesky",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "Bloqueado no Bluesky",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "Desbloquear no Bluesky",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "Carregando...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "Resultados da pesquisa",
"description": "Text for the re search modal title"
}
}

244
locales/zh/messages.json Normal file
View File

@ -0,0 +1,244 @@
{
"learn_more": {
"message": "了解更多",
"description": "Text for the learn more link"
},
"auth_factor_token": {
"message": "验证令牌",
"description": "Text for the auth factor token input"
},
"2fa_token_sent": {
"message": "双重验证令牌已发送至您的邮箱",
"description": "Text for the 2FA token sent message"
},
"find_bluesky_users": {
"message": "查找Bluesky用户",
"description": "Text for the find bluesky users button"
},
"finding_bluesky_users": {
"message": "正在查找Bluesky用户",
"description": "Text for the finding bluesky users message"
},
"password": {
"message": "密码",
"description": "Text for the password input"
},
"handle_or_email": {
"message": "用户名或邮箱",
"description": "Text for the handle or email input"
},
"recommended_to_use_app_password": {
"message": "建议使用[应用密码](https://bsky.app/settings/app-passwords)",
"description": "Text for the recommended to use app password message"
},
"error_enter_password": {
"message": "错误:请输入密码",
"description": "Text for the error message when the password is not entered"
},
"error_enter_identifier": {
"message": "错误:请输入用户名或邮箱",
"description": "Text for the error message when the handle or email is not entered"
},
"error_enter_identifier_and_password": {
"message": "错误:请输入用户名或邮箱和密码",
"description": "Text for the error message when the handle or email and password are not entered"
},
"error_enter_auth_factor_token": {
"message": "错误:请输入验证令牌",
"description": "Text for the error message when the auth factor token is not entered"
},
"error_invalid_identifier_or_password": {
"message": "错误:用户名或密码无效",
"description": "Text for the error message when the handle or password is invalid"
},
"error_invalid_page": {
"message": "错误:无效页面。请打开目标页面",
"description": "Text for the error message when the page is invalid"
},
"error_something_went_wrong": {
"message": "错误:出现问题。请刷新网页并重试",
"description": "Text for the error message when something went wrong"
},
"error_invalid_page_in_threads": {
"message": "错误:无效页面。请打开关注者或正在关注的视图",
"description": "Text for the error message when the page is invalid in threads"
},
"scanning_users": {
"message": "正在扫描$service$用户以查找Bluesky用户...",
"description": "Text for the scanning users message",
"placeholders": {
"service": {
"content": "$1",
"example": "X"
}
}
},
"detected_users": {
"message": "发现<span class='text-4xl'>$users$</span>位用户",
"description": "Text for the detected users message",
"placeholders": {
"users": {
"content": "$1"
}
}
},
"stop_scanning_and_view_results": {
"message": "停止扫描并查看结果",
"description": "Text for the stop scanning and view results button"
},
"resume_scanning": {
"message": "继续扫描",
"description": "Text for the resume scanning button"
},
"view_detected_users": {
"message": "查看检测到的用户",
"description": "Text for the view detected users button"
},
"follow_all_confirmation_title": {
"message": "是否继续执行?",
"description": "Text for the follow all confirmation title"
},
"follow_all_confirmation_message": {
"message": "用户检测并非完美,可能包含误报",
"description": "Text for the follow all confirmation message"
},
"import_list_confirmation_title": {
"message": "是否继续执行?",
"description": "Text for the import list confirmation title"
},
"import_list_confirmation_message": {
"message": "导入列表将创建一个新列表并添加所有检测到的用户。此功能为实验性功能,可能无法按预期工作",
"description": "Text for the import list confirmation message"
},
"confirmation_cancel": {
"message": "取消",
"description": "Text for the confirmation cancel button"
},
"confirmation_ok": {
"message": "确定",
"description": "Text for the confirmation ok button"
},
"toast_pending": {
"message": "处理中...",
"description": "Text for the toast pending message"
},
"toast_follow_all_success": {
"message": "已关注$count$个用户 🎉",
"description": "Text for the toast follow all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_block_all_success": {
"message": "已屏蔽$count$个用户🎉",
"description": "Text for the toast block all success message",
"placeholders": {
"count": {
"content": "$1"
}
}
},
"toast_import_list_success_view_list": {
"message": "查看导入的列表",
"description": "Text for the toast import list success view list button"
},
"toast_import_list_success": {
"message": "列表导入成功🎉",
"description": "Text for the toast import list success message"
},
"toast_import_list_error": {
"message": "导入列表失败:$error$",
"description": "Text for the toast import list error message",
"placeholders": {
"error": {
"content": "$1"
}
}
},
"source": {
"message": "来源",
"description": "Text for the source"
},
"detected": {
"message": "已检测",
"description": "Text for the detected"
},
"warning_user_detection": {
"message": "用户检测并非完美,可能包含误报",
"description": "Text for the warning user detection"
},
"donate_message": {
"message": "此工具由个人开发。您的[支持](https://ko-fi.com/kawamataryo)有助于维护和改进它。",
"description": "Text for the donate message"
},
"follow_all": {
"message": "关注全部",
"description": "Text for the follow all button"
},
"block_all": {
"message": "屏蔽全部",
"description": "Text for the block all button"
},
"import_list": {
"message": "导入列表",
"description": "Text for the import list button"
},
"followed_users": {
"message": "已关注的用户",
"description": "Text for the followed users"
},
"same_handle_name": {
"message": "相同用户名",
"description": "Text for the same handle name"
},
"same_display_name": {
"message": "相同显示名称",
"description": "Text for the same display name"
},
"included_handle_in_description": {
"message": "简介中包含用户名",
"description": "Text for the included handle in description"
},
"blocked_user": {
"message": "已屏蔽的用户",
"description": "Text for the blocked user"
},
"sidebar_detected_users": {
"message": "检测到的用户",
"description": "Text for the sidebar detected users"
},
"button_follow_on_bluesky": {
"message": "在Bluesky上关注",
"description": "Text for the button follow on bluesky"
},
"button_following_on_bluesky": {
"message": "在Bluesky上关注中",
"description": "Text for the button following on bluesky"
},
"button_unfollow_on_bluesky": {
"message": "在Bluesky上取消关注",
"description": "Text for the button unfollow on bluesky"
},
"button_block_on_bluesky": {
"message": "在Bluesky上屏蔽",
"description": "Text for the button block on bluesky"
},
"button_blocking_on_bluesky": {
"message": "在Bluesky上屏蔽中",
"description": "Text for the button blocking on bluesky"
},
"button_unblock_on_bluesky": {
"message": "在Bluesky上取消屏蔽",
"description": "Text for the button unblock on bluesky"
},
"loading": {
"message": "加载中...",
"description": "Text for the loading message"
},
"re_search_modal_title": {
"message": "搜索结果",
"description": "Text for the re search modal title"
}
}

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "sky-follower-bridge",
"version": "1.5.0",
"version": "2.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "sky-follower-bridge",
"version": "1.5.0",
"version": "2.1.0",
"dependencies": {
"@atproto/api": "^0.13.12",
"@changesets/cli": "^2.27.1",

View File

@ -1,7 +1,7 @@
{
"name": "sky-follower-bridge",
"displayName": "Sky Follower Bridge",
"version": "2.0.1",
"version": "2.1.0",
"description": "Instantly find and follow the same users from your Twitter follows on Bluesky.",
"author": "kawamataryou",
"scripts": {
@ -64,6 +64,7 @@
"typescript": "5.3.3"
},
"manifest": {
"default_locale": "en",
"host_permissions": [
"https://bsky.social/*",
"https://twitter.com/*",

View File

@ -26,12 +26,14 @@ const ReSearchModal: React.FC<ReSearchModalProps> = ({
}) => {
return (
<Modal open={open} width={600} onClose={onClose}>
<h2 className="text-lg font-bold text-center py-2">Search Results</h2>
<h2 className="text-lg font-bold text-center py-2">
{chrome.i18n.getMessage("re_search_modal_title")}
</h2>
{reSearchResults.users.length === 0 && (
<div className="text-center flex justify-center items-center flex-col gap-4 mt-5">
<span className="loading loading-spinner loading-lg" />
<div className="text-center flex justify-center items-center text-sm">
Loading...
{chrome.i18n.getMessage("loading")}
</div>
</div>
)}

View File

@ -108,12 +108,18 @@ const App = () => {
<div className="flex flex-col gap-2 items-center">
{loading && (
<p className="text-lg font-bold">
Scanning {serviceName} users to find bsky users...
{chrome.i18n.getMessage("scanning_users", [serviceName])}
</p>
)}
<p className="text-2xl font-bold">
Detected <span className="text-4xl">{users.length}</span> users
</p>
<p
className="text-2xl font-bold"
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
dangerouslySetInnerHTML={{
__html: chrome.i18n.getMessage("detected_users", [
users.length.toString(),
]),
}}
/>
{errorMessage && <AlertError>{errorMessage}</AlertError>}
{loading && (
<>
@ -122,7 +128,7 @@ const App = () => {
className="btn btn-primary mt-5 btn-ghost"
onClick={stopAndShowDetectedUsers}
>
Stop Scanning and View Results
{chrome.i18n.getMessage("stop_scanning_and_view_results")}
</button>
<LoadingCards />
</>
@ -133,7 +139,7 @@ const App = () => {
className="btn btn-primary mt-5"
onClick={restart}
>
Resume Scanning
{chrome.i18n.getMessage("resume_scanning")}
</button>
)}
{!loading && isBottomReached && (
@ -143,14 +149,14 @@ const App = () => {
className="btn btn-primary mt-5"
onClick={openOptionPage}
>
View Detected Users
{chrome.i18n.getMessage("view_detected_users")}
</button>
<button
type="button"
className="btn btn-primary mt-5 btn-ghost"
onClick={restart}
>
Resume Scanning
{chrome.i18n.getMessage("resume_scanning")}
</button>
</div>
)}

View File

@ -22,7 +22,7 @@ const AsyncButton = ({ onClick, label, className }: Props) => {
onClick={handleClick}
disabled={loading}
>
{loading ? "Processing..." : label}
{loading ? chrome.i18n.getMessage("loading") : label}
</button>
);
};

View File

@ -29,16 +29,16 @@ const DetectedUserListItem = ({
match(actionMode)
.with(ACTION_MODE.FOLLOW, ACTION_MODE.IMPORT_LIST, () => {
const follow = {
label: "Follow on Bluesky",
label: chrome.i18n.getMessage("button_follow_on_bluesky"),
class: "btn-primary",
};
const following = {
label: "Following on Bluesky",
label: chrome.i18n.getMessage("button_following_on_bluesky"),
class:
"btn-outline hover:bg-transparent hover:border hover:bg-transparent hover:text-base-content",
};
const unfollow = {
label: "Unfollow on Bluesky",
label: chrome.i18n.getMessage("button_unfollow_on_bluesky"),
class:
"text-red-500 hover:bg-transparent hover:border hover:border-red-500",
};
@ -52,16 +52,16 @@ const DetectedUserListItem = ({
})
.with(ACTION_MODE.BLOCK, () => {
const block = {
label: "Block on Bluesky",
label: chrome.i18n.getMessage("button_block_on_bluesky"),
class: "btn-primary",
};
const blocking = {
label: "Blocking on Bluesky",
label: chrome.i18n.getMessage("button_blocking_on_bluesky"),
class:
"btn-outline hover:bg-transparent hover:border hover:bg-transparent hover:text-base-content",
};
const unblock = {
label: "Unblock on Bluesky",
label: chrome.i18n.getMessage("button_unblock_on_bluesky"),
class:
"text-red-500 hover:bg-transparent hover:border hover:border-red-500",
};
@ -106,12 +106,12 @@ const DetectedUserListItem = ({
<div>
<div className={`w-full border-l-8 border-${matchTypeColor}`}>
<div
className={`w-full border-t border-gray-500 text-${matchTypeColor} grid grid-cols-[22%_1fr]`}
className={`w-full border-t border-gray-500 text-${matchTypeColor} grid grid-cols-[22%_1fr] text-xs`}
>
<div className="px-3 bg-slate-100 dark:bg-slate-800">
<div className="px-3 bg-slate-100 dark:bg-slate-800" />
<div className="px-3">
{MATCH_TYPE_LABEL_AND_COLOR[user.matchType].label}
</div>
<div className="px-3" />
</div>
<div className="bg-base-100 w-full relative grid grid-cols-[22%_1fr] gap-5">
<DetectedUserSource user={user} />

View File

@ -1,5 +1,6 @@
import React from "react";
import { match } from "ts-pattern";
import { getMessageWithLink } from "~lib/utils";
import type { MatchType, MatchTypeFilterValue } from "../../types";
import {
ACTION_MODE,
@ -78,18 +79,19 @@ const Sidebar = ({
</svg>
</div>
<div className="stat-title text-lg text-base-content font-bold">
Detected users
{chrome.i18n.getMessage("sidebar_detected_users")}
</div>
<div className="stat-value text-base-content">{detectedCount}</div>
<div className="stat-desc">
Same handle name: {matchTypeStats[BSKY_USER_MATCH_TYPE.HANDLE]}
{chrome.i18n.getMessage("same_handle_name")}:{" "}
{matchTypeStats[BSKY_USER_MATCH_TYPE.HANDLE]}
</div>
<div className="stat-desc">
Same display name:{" "}
{chrome.i18n.getMessage("same_display_name")}:{" "}
{matchTypeStats[BSKY_USER_MATCH_TYPE.DISPLAY_NAME]}
</div>
<div className="stat-desc">
Included handle in description:{" "}
{chrome.i18n.getMessage("included_handle_in_description")}:{" "}
{matchTypeStats[BSKY_USER_MATCH_TYPE.DESCRIPTION]}
</div>
</div>
@ -118,7 +120,7 @@ const Sidebar = ({
<span className="text-sm">
{key === BSKY_USER_MATCH_TYPE.FOLLOWING &&
actionMode === ACTION_MODE.BLOCK
? "Blocked users"
? chrome.i18n.getMessage("blocked_user")
: MATCH_TYPE_LABEL_AND_COLOR[key].label}
</span>
<input
@ -151,30 +153,39 @@ const Sidebar = ({
</div>
{match(actionMode)
.with(ACTION_MODE.FOLLOW, () => (
<AsyncButton onClick={followAll} label="Follow All" />
<AsyncButton
onClick={followAll}
label={chrome.i18n.getMessage("follow_all")}
/>
))
.with(ACTION_MODE.BLOCK, () => (
<AsyncButton onClick={blockAll} label="Block All" />
<AsyncButton
onClick={blockAll}
label={chrome.i18n.getMessage("block_all")}
/>
))
.with(ACTION_MODE.IMPORT_LIST, () => (
<AsyncButton onClick={importList} label="Import List" />
<AsyncButton
onClick={importList}
label={chrome.i18n.getMessage("import_list")}
/>
))
.otherwise(() => null)}
<p className="text-xs">
User detection is not perfect and may include false positives.
{chrome.i18n.getMessage("warning_user_detection")}
</p>
</div>
<div className="mt-auto">
<div className="divider" />
<p className="mb-2">
If you find this tool helpful, I'd appreciate{" "}
<a href="https://ko-fi.com/X8X315UWFN" className="link">
your support
</a>{" "}
to help me maintain and improve it
</p>
<p
className="mb-2 text-xs"
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
dangerouslySetInnerHTML={{
__html: getMessageWithLink("donate_message"),
}}
/>
<a
href="https://ko-fi.com/X8X315UWFN"
href="https://ko-fi.com/kawamataryo"
target="_blank"
rel="noreferrer"
style={{ display: "inline-block" }}

View File

@ -75,19 +75,19 @@ export const MAX_RELOAD_COUNT = 1;
export const MATCH_TYPE_LABEL_AND_COLOR = {
[BSKY_USER_MATCH_TYPE.HANDLE]: {
label: "Same handle name",
label: chrome.i18n.getMessage("same_handle_name"),
color: "info",
},
[BSKY_USER_MATCH_TYPE.DISPLAY_NAME]: {
label: "Same display name",
label: chrome.i18n.getMessage("same_display_name"),
color: "warning",
},
[BSKY_USER_MATCH_TYPE.DESCRIPTION]: {
label: "Included handle in description",
label: chrome.i18n.getMessage("included_handle_in_description"),
color: "secondary",
},
[BSKY_USER_MATCH_TYPE.FOLLOWING]: {
label: "Followed users",
label: chrome.i18n.getMessage("followed_users"),
color: "success",
},
};
@ -95,6 +95,9 @@ export const MATCH_TYPE_LABEL_AND_COLOR = {
export const AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE =
"AuthFactorTokenRequiredError";
export const INVALID_IDENTIFIER_OR_PASSWORD_ERROR_MESSAGE =
"Invalid identifier or password";
export const RATE_LIMIT_ERROR_MESSAGE = "Rate limit";
export const DOCUMENT_LINK = {

View File

@ -28,10 +28,7 @@ export class ThreadsService extends AbstractService {
'[role="dialog"] [role="tab"]>[role="button"]',
);
if (!isTargetPage) {
return [
false,
"Invalid page. please open the following or followers view.",
];
return [false, chrome.i18n.getMessage("error_invalid_page_in_threads")];
}
return [true, ""];
}

View File

@ -36,3 +36,21 @@ export const findFirstScrollableElements = (
return scrollableElements[0] ?? null;
};
export const getMessageWithLink = (
key: string,
placeholders: string[] = [],
) => {
const linkPattern = /\[(.*?)\]\((.*?)\)/g;
let message = chrome.i18n.getMessage(key, placeholders);
const links = message.matchAll(linkPattern);
for (const link of links) {
const [fullMatch, text, url] = link;
message = message.replace(
fullMatch,
`<a href="${url}" target="_blank" class="link" rel="noreferrer">${text}</a>`,
);
}
return message;
};

View File

@ -31,22 +31,20 @@ const Option = () => {
confirm: followAllConfirm,
ConfirmationDialog: FollowAllConfirmationDialog,
} = useConfirm({
title: "Proceed with Execution?",
message:
"User detection is not perfect and may include false positives. Do you still want to proceed?",
cancelText: "Cancel",
okText: "OK",
title: chrome.i18n.getMessage("follow_all_confirmation_title"),
message: chrome.i18n.getMessage("follow_all_confirmation_message"),
cancelText: chrome.i18n.getMessage("confirmation_cancel"),
okText: chrome.i18n.getMessage("confirmation_ok"),
});
const {
confirm: importListConfirm,
ConfirmationDialog: ImportListConfirmationDialog,
} = useConfirm({
title: "Proceed with Execution?",
message:
"Importing a list will create a new list and add all detected users to it. This feature is experimental and may not work as expected. Do you still want to proceed?",
cancelText: "Cancel",
okText: "OK",
title: chrome.i18n.getMessage("import_list_confirmation_title"),
message: chrome.i18n.getMessage("import_list_confirmation_message"),
cancelText: chrome.i18n.getMessage("confirmation_cancel"),
okText: chrome.i18n.getMessage("confirmation_ok"),
});
const handleFollowAll = async () => {
@ -54,10 +52,16 @@ const Option = () => {
return;
}
toast.promise(followAll, {
pending: "Processing...",
pending: chrome.i18n.getMessage("toast_pending"),
success: {
render({ data }) {
return <span className="font-bold">Followed {data} users🎉</span>;
return (
<span className="font-bold">
{chrome.i18n.getMessage("toast_follow_all_success", [
data.toString(),
])}
</span>
);
},
},
});
@ -68,10 +72,16 @@ const Option = () => {
return;
}
toast.promise(blockAll, {
pending: "Processing...",
pending: chrome.i18n.getMessage("toast_pending"),
success: {
render({ data }) {
return <span className="font-bold">Blocked {data} users🎉</span>;
return (
<span className="font-bold">
{chrome.i18n.getMessage("toast_block_all_success", [
data.toString(),
])}
</span>
);
},
},
});
@ -82,15 +92,17 @@ const Option = () => {
return;
}
toast.promise(importList, {
pending: "Processing...",
pending: chrome.i18n.getMessage("toast_pending"),
success: {
render({ data }) {
return (
<>
<span className="font-bold">List imported successfully🎉</span>
<span className="font-bold">
{chrome.i18n.getMessage("toast_import_list_success")}
</span>
<br />
<a href={data} target="_blank" rel="noreferrer" className="link">
View Imported List
{chrome.i18n.getMessage("toast_import_list_success_view_list")}
</a>
</>
);
@ -98,8 +110,9 @@ const Option = () => {
},
error: {
render({ data }) {
console.log(data);
return `Failed to import list: ${data}`;
return chrome.i18n.getMessage("toast_import_list_error", [
data as string,
]);
},
},
});
@ -153,8 +166,12 @@ const Option = () => {
</div>
<div className="flex-1 ml-80 p-6 pt-0 overflow-y-auto">
<div className="grid grid-cols-[22%_1fr] sticky top-0 z-10 bg-base-100 border-b-[1px] border-gray-500">
<h2 className="text-lg font-bold text-center py-2">Source</h2>
<h2 className="text-lg font-bold text-center py-2">Detected</h2>
<h2 className="text-lg font-bold text-center py-2">
{chrome.i18n.getMessage("source")}
</h2>
<h2 className="text-lg font-bold text-center py-2">
{chrome.i18n.getMessage("detected")}
</h2>
</div>
<div className="flex flex-col border-b-[1px] border-gray-500">
{filteredUsers.map((user) => (

View File

@ -10,6 +10,7 @@ import {
AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE,
BSKY_DOMAIN,
DOCUMENT_LINK,
INVALID_IDENTIFIER_OR_PASSWORD_ERROR_MESSAGE,
MAX_RELOAD_COUNT,
MESSAGE_NAMES,
MESSAGE_TYPE,
@ -17,6 +18,7 @@ import {
STORAGE_KEYS,
TARGET_URLS_REGEX,
} from "~lib/constants";
import { getMessageWithLink } from "~lib/utils";
function IndexPopup() {
const [isLoading, setIsLoading] = useState(false);
@ -81,19 +83,21 @@ function IndexPopup() {
const validateForm = () => {
if (!password && !identifier) {
setErrorMessage("Error: Please enter your password and identifier.");
setErrorMessage(
chrome.i18n.getMessage("error_enter_identifier_and_password"),
);
return false;
}
if (!password) {
setErrorMessage("Error: Please enter your password.");
setErrorMessage(chrome.i18n.getMessage("error_enter_password"));
return false;
}
if (!identifier) {
setErrorMessage("Error: Please enter your identifier.");
setErrorMessage(chrome.i18n.getMessage("error_enter_identifier"));
return false;
}
if (isShowAuthFactorTokenInput && !authFactorToken) {
setErrorMessage("Error: Please enter your auth factor token.");
setErrorMessage(chrome.i18n.getMessage("error_enter_auth_factor_token"));
return false;
}
return true;
@ -115,7 +119,7 @@ function IndexPopup() {
if (!Object.values(TARGET_URLS_REGEX).some((r) => r.test(currentUrl))) {
setErrorMessage(
"Error: Invalid page. please open the target page.",
chrome.i18n.getMessage("error_invalid_page"),
DOCUMENT_LINK.PAGE_ERROR,
);
return;
@ -166,6 +170,13 @@ function IndexPopup() {
await saveShowAuthFactorTokenInputToStorage(true);
} else if (error.message.includes(RATE_LIMIT_ERROR_MESSAGE)) {
setErrorMessage(error.message, DOCUMENT_LINK.RATE_LIMIT_ERROR);
} else if (
error.message.includes(INVALID_IDENTIFIER_OR_PASSWORD_ERROR_MESSAGE)
) {
setErrorMessage(
chrome.i18n.getMessage("error_invalid_identifier_or_password"),
DOCUMENT_LINK.LOGIN_ERROR,
);
} else {
setErrorMessage(error.message, DOCUMENT_LINK.LOGIN_ERROR);
}
@ -202,7 +213,7 @@ function IndexPopup() {
await searchBskyUser();
} else {
setErrorMessage(
"Error: Something went wrong. Please reload the web page and try again.",
chrome.i18n.getMessage("error_something_went_wrong"),
DOCUMENT_LINK.OTHER_ERROR,
);
}
@ -258,7 +269,7 @@ function IndexPopup() {
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
Handle or Email
{chrome.i18n.getMessage("handle_or_email")}
</div>
<input
type="text"
@ -286,20 +297,17 @@ function IndexPopup() {
/>
</svg>
<p>
Password
{chrome.i18n.getMessage("password")}
<br />
</p>
</div>
<span className="text-xs">
We recommend using the{" "}
<a
href="https://bsky.app/settings/app-passwords"
target="_blank"
rel="noreferrer"
className="link"
>
App Password.
</a>
<span
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
dangerouslySetInnerHTML={{
__html: getMessageWithLink("recommended_to_use_app_password"),
}}
/>
</span>
<input
type="password"
@ -322,8 +330,8 @@ function IndexPopup() {
className="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z"
/>
</svg>
@ -350,10 +358,12 @@ function IndexPopup() {
disabled={isLoading}
>
{isLoading && <span className="w-4 loading loading-spinner" />}
{isLoading ? "Finding Bluesky Users" : "Find Bluesky Users"}
{isLoading
? chrome.i18n.getMessage("finding_bluesky_users")
: chrome.i18n.getMessage("find_bluesky_users")}
</button>
{isShowErrorMessage && (
<div className="flex gap-2 items-center text-red-600 border border-red-600 p-2 rounded-md mt-2">
<div className="flex gap-2 items-center text-red-600 border border-red-600 p-2 rounded-md mt-2 text-xs">
<svg
xmlns="http://www.w3.org/2000/svg"
className="stroke-current flex-shrink-0 h-6 w-6"
@ -376,10 +386,9 @@ function IndexPopup() {
rel="noreferrer"
className="link ml-2"
>
Learn more
{chrome.i18n.getMessage("learn_more")}
</a>
)}
.
</span>
</div>
)}

View File

@ -8,7 +8,13 @@ module.exports = {
safelist: [
{
pattern: /(badge|border|bg|text|border-b)-(info|warning|secondary|neutral|success)/,
}
},
{
pattern: /(link)/,
},
{
pattern: /(text-4xl)/,
},
],
daisyui: {
themes: [