From fc91c4d3a2b95357f04896ecc82ee58ec84a4293 Mon Sep 17 00:00:00 2001 From: kawamataryo Date: Sat, 7 Dec 2024 22:40:33 +0900 Subject: [PATCH] feat: add i18n --- .storybook/preview.ts | 11 + locales/en/messages.json | 244 ++++++++++++++++++++ locales/es/messages.json | 244 ++++++++++++++++++++ locales/fr/messages.json | 244 ++++++++++++++++++++ locales/it/messages.json | 244 ++++++++++++++++++++ locales/ja/messages.json | 244 ++++++++++++++++++++ locales/pt/messages.json | 244 ++++++++++++++++++++ locales/zh/messages.json | 244 ++++++++++++++++++++ package-lock.json | 4 +- package.json | 3 +- src/components/ReSearchModal.tsx | 6 +- src/contents/App.tsx | 22 +- src/lib/components/AsyncButton.tsx | 2 +- src/lib/components/DetectedUserListItem.tsx | 18 +- src/lib/components/Sidebar.tsx | 45 ++-- src/lib/constants.ts | 11 +- src/lib/services/threadsService.ts | 5 +- src/lib/utils.ts | 18 ++ src/options.tsx | 59 +++-- src/popup.tsx | 55 +++-- tailwind.config.js | 8 +- 21 files changed, 1882 insertions(+), 93 deletions(-) create mode 100644 locales/en/messages.json create mode 100644 locales/es/messages.json create mode 100644 locales/fr/messages.json create mode 100644 locales/it/messages.json create mode 100644 locales/ja/messages.json create mode 100644 locales/pt/messages.json create mode 100644 locales/zh/messages.json diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 434310d..6d723eb 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -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: { diff --git a/locales/en/messages.json b/locales/en/messages.json new file mode 100644 index 0000000..4a18875 --- /dev/null +++ b/locales/en/messages.json @@ -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 $users$ 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" + } +} diff --git a/locales/es/messages.json b/locales/es/messages.json new file mode 100644 index 0000000..440a859 --- /dev/null +++ b/locales/es/messages.json @@ -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 $users$ 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" + } +} diff --git a/locales/fr/messages.json b/locales/fr/messages.json new file mode 100644 index 0000000..dba59e7 --- /dev/null +++ b/locales/fr/messages.json @@ -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": "$users$ 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" + } +} diff --git a/locales/it/messages.json b/locales/it/messages.json new file mode 100644 index 0000000..f3edde3 --- /dev/null +++ b/locales/it/messages.json @@ -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 $users$ 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" + } +} diff --git a/locales/ja/messages.json b/locales/ja/messages.json new file mode 100644 index 0000000..a7d369b --- /dev/null +++ b/locales/ja/messages.json @@ -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": "$users$ 人を検出", + "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" + } +} diff --git a/locales/pt/messages.json b/locales/pt/messages.json new file mode 100644 index 0000000..cfe1d97 --- /dev/null +++ b/locales/pt/messages.json @@ -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 $users$ 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" + } +} diff --git a/locales/zh/messages.json b/locales/zh/messages.json new file mode 100644 index 0000000..b7cb1ac --- /dev/null +++ b/locales/zh/messages.json @@ -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": "发现$users$位用户", + "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" + } +} diff --git a/package-lock.json b/package-lock.json index cdf5f19..2ddc739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 05dbb05..1ee729d 100644 --- a/package.json +++ b/package.json @@ -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/*", diff --git a/src/components/ReSearchModal.tsx b/src/components/ReSearchModal.tsx index 41f62aa..3491185 100644 --- a/src/components/ReSearchModal.tsx +++ b/src/components/ReSearchModal.tsx @@ -26,12 +26,14 @@ const ReSearchModal: React.FC = ({ }) => { return ( -

Search Results

+

+ {chrome.i18n.getMessage("re_search_modal_title")} +

{reSearchResults.users.length === 0 && (
- Loading... + {chrome.i18n.getMessage("loading")}
)} diff --git a/src/contents/App.tsx b/src/contents/App.tsx index 247740a..5608279 100644 --- a/src/contents/App.tsx +++ b/src/contents/App.tsx @@ -108,12 +108,18 @@ const App = () => {
{loading && (

- Scanning {serviceName} users to find bsky users... + {chrome.i18n.getMessage("scanning_users", [serviceName])}

)} -

- Detected {users.length} users -

+

+ dangerouslySetInnerHTML={{ + __html: chrome.i18n.getMessage("detected_users", [ + users.length.toString(), + ]), + }} + /> {errorMessage && {errorMessage}} {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")} @@ -133,7 +139,7 @@ const App = () => { className="btn btn-primary mt-5" onClick={restart} > - Resume Scanning + {chrome.i18n.getMessage("resume_scanning")} )} {!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")}

)} diff --git a/src/lib/components/AsyncButton.tsx b/src/lib/components/AsyncButton.tsx index 351f692..9c7362d 100644 --- a/src/lib/components/AsyncButton.tsx +++ b/src/lib/components/AsyncButton.tsx @@ -22,7 +22,7 @@ const AsyncButton = ({ onClick, label, className }: Props) => { onClick={handleClick} disabled={loading} > - {loading ? "Processing..." : label} + {loading ? chrome.i18n.getMessage("loading") : label} ); }; diff --git a/src/lib/components/DetectedUserListItem.tsx b/src/lib/components/DetectedUserListItem.tsx index 46a06de..54cda82 100644 --- a/src/lib/components/DetectedUserListItem.tsx +++ b/src/lib/components/DetectedUserListItem.tsx @@ -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 = ({
-
+
+
{MATCH_TYPE_LABEL_AND_COLOR[user.matchType].label}
-
diff --git a/src/lib/components/Sidebar.tsx b/src/lib/components/Sidebar.tsx index 7116448..cc9c289 100644 --- a/src/lib/components/Sidebar.tsx +++ b/src/lib/components/Sidebar.tsx @@ -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 = ({
- Detected users + {chrome.i18n.getMessage("sidebar_detected_users")}
{detectedCount}
- Same handle name: {matchTypeStats[BSKY_USER_MATCH_TYPE.HANDLE]} + {chrome.i18n.getMessage("same_handle_name")}:{" "} + {matchTypeStats[BSKY_USER_MATCH_TYPE.HANDLE]}
- Same display name:{" "} + {chrome.i18n.getMessage("same_display_name")}:{" "} {matchTypeStats[BSKY_USER_MATCH_TYPE.DISPLAY_NAME]}
- Included handle in description:{" "} + {chrome.i18n.getMessage("included_handle_in_description")}:{" "} {matchTypeStats[BSKY_USER_MATCH_TYPE.DESCRIPTION]}
@@ -118,7 +120,7 @@ const Sidebar = ({ {key === BSKY_USER_MATCH_TYPE.FOLLOWING && actionMode === ACTION_MODE.BLOCK - ? "Blocked users" + ? chrome.i18n.getMessage("blocked_user") : MATCH_TYPE_LABEL_AND_COLOR[key].label} {match(actionMode) .with(ACTION_MODE.FOLLOW, () => ( - + )) .with(ACTION_MODE.BLOCK, () => ( - + )) .with(ACTION_MODE.IMPORT_LIST, () => ( - + )) .otherwise(() => null)}

- ⚠️ User detection is not perfect and may include false positives. + ⚠️ {chrome.i18n.getMessage("warning_user_detection")}

-

- If you find this tool helpful, I'd appreciate{" "} - - your support - {" "} - to help me maintain and improve it ☕ -

+

+ dangerouslySetInnerHTML={{ + __html: getMessageWithLink("donate_message"), + }} + /> [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, ""]; } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a04ab3e..3fe9d92 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -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, + `${text}`, + ); + } + + return message; +}; diff --git a/src/options.tsx b/src/options.tsx index ce1131e..2c81109 100644 --- a/src/options.tsx +++ b/src/options.tsx @@ -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 Followed {data} users🎉; + return ( + + {chrome.i18n.getMessage("toast_follow_all_success", [ + data.toString(), + ])} + + ); }, }, }); @@ -68,10 +72,16 @@ const Option = () => { return; } toast.promise(blockAll, { - pending: "Processing...", + pending: chrome.i18n.getMessage("toast_pending"), success: { render({ data }) { - return Blocked {data} users🎉; + return ( + + {chrome.i18n.getMessage("toast_block_all_success", [ + data.toString(), + ])} + + ); }, }, }); @@ -82,15 +92,17 @@ const Option = () => { return; } toast.promise(importList, { - pending: "Processing...", + pending: chrome.i18n.getMessage("toast_pending"), success: { render({ data }) { return ( <> - List imported successfully🎉 + + {chrome.i18n.getMessage("toast_import_list_success")} +
- View Imported List + {chrome.i18n.getMessage("toast_import_list_success_view_list")} ); @@ -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 = () => {

-

Source

-

Detected

+

+ {chrome.i18n.getMessage("source")} +

+

+ {chrome.i18n.getMessage("detected")} +

{filteredUsers.map((user) => ( diff --git a/src/popup.tsx b/src/popup.tsx index 102495c..3eac655 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -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" /> - Handle or Email + {chrome.i18n.getMessage("handle_or_email")}

- Password + {chrome.i18n.getMessage("password")}

- We recommend using the{" "} - - App Password. - + + dangerouslySetInnerHTML={{ + __html: getMessageWithLink("recommended_to_use_app_password"), + }} + /> @@ -350,10 +358,12 @@ function IndexPopup() { disabled={isLoading} > {isLoading && } - {isLoading ? "Finding Bluesky Users" : "Find Bluesky Users"} + {isLoading + ? chrome.i18n.getMessage("finding_bluesky_users") + : chrome.i18n.getMessage("find_bluesky_users")} {isShowErrorMessage && ( -
+
- Learn more + {chrome.i18n.getMessage("learn_more")} )} - .
)} diff --git a/tailwind.config.js b/tailwind.config.js index e343277..9f935f7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -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: [