From 80898ee2fe998c944a42047a09b64ebede92881f Mon Sep 17 00:00:00 2001 From: c0mrade Date: Tue, 26 May 2026 20:36:19 +0300 Subject: [PATCH] clarify(locales): sweep em-dashes from copy across 4 locales MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit P1: 40 em-dash hits in src/locales/. Impeccable rule: 'No em dashes. Use commas, colons, semicolons, periods, or parentheses.' Sentence-level em-dashes obscure structure; explicit punctuation parses faster. 30 em-dashes replaced across en/ru/zh/fa: • 'X — Y' joining a clause → 'X, Y' • 'X — Y' introducing value/detail → 'X: Y' ('X:Y' in zh) • 'X — Y' sentence-ending fragment → 'X. Y' ('X。Y' in zh) Affected keys (same in all 4 locales unless noted): telegramReopenHint (ru only) freeDesc searchResults openUrlDirectHint deleteSuccessWithSubscriptions promoGroupsHint activePaidWarning (ru only) errors.title activateSuccessDesc noBonusDescription (en, zh, fa — no ru key) campaignRegistrations (en, zh, fa) 10 em-dashes kept intentionally (not copy violations): • noTariff: '—' (4 locales) — IS the value placeholder, not text • previewEmpty: '— empty —' (ru + en) — decorative brackets framing 'empty' • statusLegend: '✅ — X • ➕ — Y …' (4 locales) — legend convention, icon→label separator; switching to ':' would need a wider rework of the visual rhythm All 4 JSON files parse cleanly; TSC/lint/build green. --- src/locales/en.json | 18 +++++++++--------- src/locales/fa.json | 12 ++++++------ src/locales/ru.json | 18 +++++++++--------- src/locales/zh.json | 12 ++++++------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 1627841..8bf5b24 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -282,7 +282,7 @@ "trialOffer": { "freeTitle": "Free Trial Available", "paidTitle": "Trial Subscription", - "freeDesc": "Try our VPN for free — no commitment required", + "freeDesc": "Try our VPN for free, no commitment required", "paidDesc": "Get started with a trial subscription" }, "stats": { @@ -1475,7 +1475,7 @@ "description": "Search and manage payments", "searchPlaceholder": "Search: invoice, TG ID, @username, email...", "searchHint": "Examples: SP-78291, @username, 123456789, user@mail.ru", - "searchResults": "Results for: {{query}} — found {{count}}", + "searchResults": "Results for: {{query}}. Found {{count}}", "resetSearch": "Reset", "statusAll": "All", "statusPending": "Pending", @@ -1521,7 +1521,7 @@ "methodEnabled": "Method enabled", "providerNotConfigured": "Provider not configured in env", "openUrlDirect": "Open payment page directly", - "openUrlDirectHint": "Skip the link panel — the provider opens inside the MiniApp/tab right after the click. After payment the user returns to /balance/top-up/result. Ignored for Stars/CryptoBot and other t.me/ links.", + "openUrlDirectHint": "Skip the link panel: the provider opens inside the MiniApp/tab right after the click. After payment the user returns to /balance/top-up/result. Ignored for Stars/CryptoBot and other t.me/ links.", "displayName": "Display name", "displayNameHint": "Leave empty for default name", "subOptions": "Payment options", @@ -2151,7 +2151,7 @@ "summaryErrors": "{{count}} errors" }, "errors": { - "title": "{{count}} errors — show details" + "title": "{{count}} errors. Show details" }, "filters": { "search": "Search by ID, username, email", @@ -2398,7 +2398,7 @@ "confirmDeleteText": "This action cannot be undone. The tariff will be permanently deleted.", "confirmDeleteWithSubscriptions": "This tariff has {{count}} active subscriptions. After deletion, users will need to select a new tariff when renewing.", "deleteSuccess": "Tariff deleted successfully", - "deleteSuccessWithSubscriptions": "Tariff deleted. {{count}} subscriptions reset — users will choose a new tariff on renewal.", + "deleteSuccessWithSubscriptions": "Tariff deleted. {{count}} subscriptions reset. Users will choose a new tariff on renewal.", "saveOrder": "Save order", "orderSaved": "Order saved", "dragToReorder": "Drag to reorder", @@ -2459,7 +2459,7 @@ "serversTabHint": "Select servers available on this tariff.", "noServersAvailable": "No servers available", "promoGroupsTitle": "Promo Groups", - "promoGroupsHint": "Select promo groups that have access to this tariff. If none selected — available to everyone.", + "promoGroupsHint": "Select promo groups that have access to this tariff. If none selected, available to everyone.", "noPromoGroups": "No promo groups", "extraDeviceTitle": "Additional devices", "devicePriceLabel": "Device price (30 days):", @@ -2612,7 +2612,7 @@ "notSelected": "Not selected", "tariffOption": "{{traffic}} GB, {{devices}} dev.", "durationDays": "Duration (days)", - "noBonusDescription": "Campaign without bonuses — only for tracking clicks and registrations.", + "noBonusDescription": "Campaign without bonuses: only for tracking clicks and registrations.", "active": "Active", "cancel": "Cancel", "save": "Save", @@ -2662,7 +2662,7 @@ }, "users": { "title": "Campaign Users", - "campaignRegistrations": "{{name}} — {{count}} registrations", + "campaignRegistrations": "{{name}}: {{count}} registrations", "noUsers": "No registered users", "user": "User", "bonus": "Bonus", @@ -4862,7 +4862,7 @@ "activateButton": "Activate gift", "activating": "Activating...", "activateSuccess": "Gift activated!", - "activateSuccessDesc": "{{tariff}} — {{days}} days added to your subscription", + "activateSuccessDesc": "{{tariff}}: {{days}} days added to your subscription", "activateError": "Failed to activate gift", "activateSelfError": "You cannot activate your own gift", "myGiftsEmpty": "No gifts yet", diff --git a/src/locales/fa.json b/src/locales/fa.json index f55f2f2..57e990c 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -1511,7 +1511,7 @@ "description": "جستجو و مدیریت پرداخت‌ها", "searchPlaceholder": "جستجو: فاکتور، شناسه تلگرام، @نام‌کاربری، ایمیل...", "searchHint": "مثال‌ها: SP-78291, @username, 123456789, user@mail.ru", - "searchResults": "نتایج برای: {{query}} — {{count}} یافت شد", + "searchResults": "نتایج برای: {{query}}. {{count}} یافت شد", "resetSearch": "بازنشانی", "statusAll": "همه", "statusPending": "در انتظار", @@ -1915,7 +1915,7 @@ "confirmDeleteText": "این عمل قابل بازگشت نیست. تعرفه برای همیشه حذف می‌شود.", "confirmDeleteWithSubscriptions": "این تعرفه {{count}} اشتراک فعال دارد. پس از حذف، کاربران هنگام تمدید باید تعرفه جدید انتخاب کنند.", "deleteSuccess": "تعرفه با موفقیت حذف شد", - "deleteSuccessWithSubscriptions": "تعرفه حذف شد. تعرفه {{count}} اشتراک بازنشانی شد — کاربران هنگام تمدید تعرفه جدید انتخاب می‌کنند.", + "deleteSuccessWithSubscriptions": "تعرفه حذف شد. تعرفه {{count}} اشتراک بازنشانی شد. کاربران هنگام تمدید تعرفه جدید انتخاب می‌کنند.", "saveOrder": "ذخیره ترتیب", "orderSaved": "ترتیب ذخیره شد", "dragToReorder": "برای تغییر ترتیب بکشید", @@ -2088,7 +2088,7 @@ "methodEnabled": "روش فعال است", "providerNotConfigured": "ارائه‌دهنده در env تنظیم نشده", "openUrlDirect": "صفحه پرداخت را مستقیماً باز کن", - "openUrlDirectHint": "بدون پنل لینک — ارائه‌دهنده داخل MiniApp/تب بلافاصله پس از کلیک باز می‌شود. پس از پرداخت کاربر به /balance/top-up/result بازمی‌گردد. برای Stars/CryptoBot و سایر لینک‌های t.me/ این پرچم نادیده گرفته می‌شود.", + "openUrlDirectHint": "بدون پنل لینک: ارائه‌دهنده داخل MiniApp/تب بلافاصله پس از کلیک باز می‌شود. پس از پرداخت کاربر به /balance/top-up/result بازمی‌گردد. برای Stars/CryptoBot و سایر لینک‌های t.me/ این پرچم نادیده گرفته می‌شود.", "displayName": "نام نمایشی", "displayNameHint": "برای نام پیش‌فرض خالی بگذارید", "subOptions": "گزینه‌های پرداخت", @@ -2163,7 +2163,7 @@ "notSelected": "انتخاب نشده", "tariffOption": "{{traffic}} GB، {{devices}} دستگاه", "durationDays": "مدت (روز)", - "noBonusDescription": "کمپین بدون پاداش — فقط برای ردیابی کلیک‌ها و ثبت‌نام‌ها.", + "noBonusDescription": "کمپین بدون پاداش: فقط برای ردیابی کلیک‌ها و ثبت‌نام‌ها.", "active": "فعال", "cancel": "لغو", "save": "ذخیره", @@ -2211,7 +2211,7 @@ }, "users": { "title": "کاربران کمپین", - "campaignRegistrations": "{{name}} — {{count}} ثبت‌نام", + "campaignRegistrations": "{{name}}: {{count}} ثبت‌نام", "noUsers": "کاربر ثبت‌نام شده‌ای وجود ندارد", "user": "کاربر", "bonus": "پاداش", @@ -3633,7 +3633,7 @@ "summaryErrors": "{{count}} خطا" }, "errors": { - "title": "{{count}} خطا — نمایش جزئیات" + "title": "{{count}} خطا. نمایش جزئیات" }, "filters": { "search": "جستجو بر اساس شناسه، نام کاربری، ایمیل", diff --git a/src/locales/ru.json b/src/locales/ru.json index da3ccc8..003120c 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -172,7 +172,7 @@ "registerHint": "Для регистрации по email сначала авторизуйтесь через Telegram, затем привяжите email в настройках.", "telegramRequired": "Требуется авторизация через Telegram", "telegramRetryFailed": "Авторизация не удалась. Закройте приложение и откройте заново.", - "telegramReopenHint": "Если проблема повторяется — закройте и откройте приложение заново", + "telegramReopenHint": "Если проблема повторяется, закройте и откройте приложение заново", "telegramNotConfigured": "Telegram бот не настроен", "telegramUnavailable": "Вход через Telegram временно недоступен", "authenticating": "Авторизация...", @@ -295,7 +295,7 @@ "trialOffer": { "freeTitle": "Бесплатный пробный период", "paidTitle": "Пробная подписка", - "freeDesc": "Попробуйте наш VPN бесплатно — без обязательств", + "freeDesc": "Попробуйте наш VPN бесплатно: без обязательств", "paidDesc": "Начните с пробной подписки" }, "stats": { @@ -1497,7 +1497,7 @@ "description": "Поиск и управление платежами", "searchPlaceholder": "Поиск: инвойс, TG ID, @username, email...", "searchHint": "Примеры: SP-78291, @username, 123456789, user@mail.ru", - "searchResults": "Результаты по запросу: {{query}} — найдено {{count}}", + "searchResults": "Результаты по запросу: {{query}}. Найдено {{count}}", "resetSearch": "Сбросить", "statusAll": "Все", "statusPending": "В ожидании", @@ -1543,7 +1543,7 @@ "methodEnabled": "Метод включён", "providerNotConfigured": "Провайдер не настроен в env", "openUrlDirect": "Открывать страницу оплаты сразу", - "openUrlDirectHint": "Без панели со ссылкой — провайдер открывается внутри MiniApp/вкладки сразу после клика. После оплаты юзер возвращается на /balance/top-up/result. Для Stars/CryptoBot и других t.me/-ссылок флаг игнорируется.", + "openUrlDirectHint": "Без панели со ссылкой: провайдер открывается внутри MiniApp/вкладки сразу после клика. После оплаты юзер возвращается на /balance/top-up/result. Для Stars/CryptoBot и других t.me/-ссылок флаг игнорируется.", "displayName": "Отображаемое имя", "displayNameHint": "Оставьте пустым для имени по умолчанию", "subOptions": "Варианты оплаты", @@ -2796,7 +2796,7 @@ "confirmDeleteText": "Это действие нельзя отменить. Тариф будет удалён навсегда.", "confirmDeleteWithSubscriptions": "У этого тарифа {{count}} активных подписок. При удалении пользователям потребуется выбрать новый тариф при продлении.", "deleteSuccess": "Тариф успешно удален", - "deleteSuccessWithSubscriptions": "Тариф удален. У {{count}} подписок сброшен тариф — при продлении пользователи выберут новый.", + "deleteSuccessWithSubscriptions": "Тариф удален. У {{count}} подписок сброшен тариф. При продлении пользователи выберут новый.", "saveOrder": "Сохранить порядок", "orderSaved": "Порядок сохранён", "dragToReorder": "Перетащите для изменения порядка", @@ -2857,7 +2857,7 @@ "serversTabHint": "Выберите серверы, доступные на этом тарифе.", "noServersAvailable": "Нет доступных серверов", "promoGroupsTitle": "Промо-группы", - "promoGroupsHint": "Выберите промо-группы, которым доступен этот тариф. Если не выбрано ничего — доступен всем.", + "promoGroupsHint": "Выберите промо-группы, которым доступен этот тариф. Если не выбрано ничего, доступен всем.", "noPromoGroups": "Нет промо-групп", "extraDeviceTitle": "Докупка устройств", "devicePriceLabel": "Цена за устройство (30 дней):", @@ -3880,7 +3880,7 @@ "deleteSubscription": { "warning": "Выбранные подписки будут безвозвратно удалены из бота и панели RemnaWave!", "hint": "Пользователи потеряют доступ к VPN по удалённым подпискам. Это действие нельзя отменить.", - "activePaidWarning": "{{count}} из {{total}} выбранных подписок — активные платные", + "activePaidWarning": "{{count}} из {{total}} выбранных подписок: активные платные", "forceDeleteConfirm": "Подтверждаю удаление активных платных подписок" }, "deleteUser": { @@ -3917,7 +3917,7 @@ "summaryErrors": "{{count}} ошибок" }, "errors": { - "title": "{{count}} ошибок — показать детали" + "title": "{{count}} ошибок. Показать детали" }, "filters": { "search": "Поиск по ID, нику, email", @@ -5418,7 +5418,7 @@ "activateButton": "Активировать подарок", "activating": "Активация...", "activateSuccess": "Подарок активирован!", - "activateSuccessDesc": "{{tariff}} — {{days}} дн. добавлено к вашей подписке", + "activateSuccessDesc": "{{tariff}}: {{days}} дн. добавлено к вашей подписке", "activateError": "Не удалось активировать подарок", "activateSelfError": "Нельзя активировать свой собственный подарок", "myGiftsEmpty": "У вас пока нет подарков", diff --git a/src/locales/zh.json b/src/locales/zh.json index 728d600..5575e07 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1286,7 +1286,7 @@ "methodEnabled": "方法已启用", "providerNotConfigured": "提供商未在env中配置", "openUrlDirect": "直接打开支付页面", - "openUrlDirectHint": "跳过链接面板 — 点击后提供商在 MiniApp/标签页内直接打开。支付完成后用户返回 /balance/top-up/result。Stars/CryptoBot 和其他 t.me/ 链接忽略此标志。", + "openUrlDirectHint": "跳过链接面板:点击后提供商在 MiniApp/标签页内直接打开。支付完成后用户返回 /balance/top-up/result。Stars/CryptoBot 和其他 t.me/ 链接忽略此标志。", "displayName": "显示名称", "displayNameHint": "留空以使用默认名称", "subOptions": "支付选项", @@ -1338,7 +1338,7 @@ "description": "搜索和管理支付", "searchPlaceholder": "搜索:发票、TG ID、@用户名、邮箱...", "searchHint": "示例:SP-78291、@username、123456789、user@mail.ru", - "searchResults": "搜索结果:{{query}} — 找到 {{count}} 条", + "searchResults": "搜索结果:{{query}}。找到 {{count}} 条", "resetSearch": "重置", "statusAll": "全部", "statusPending": "待处理", @@ -1955,7 +1955,7 @@ "confirmDeleteText": "此操作不可撤销。套餐将被永久删除。", "confirmDeleteWithSubscriptions": "此套餐有 {{count}} 个活跃订阅。删除后,用户续订时需要选择新套餐。", "deleteSuccess": "套餐删除成功", - "deleteSuccessWithSubscriptions": "套餐已删除。{{count}} 个订阅已重置 — 用户续订时将选择新套餐。", + "deleteSuccessWithSubscriptions": "套餐已删除。{{count}} 个订阅已重置。用户续订时将选择新套餐。", "saveOrder": "保存排序", "orderSaved": "排序已保存", "dragToReorder": "拖动以重新排序", @@ -2162,7 +2162,7 @@ "notSelected": "未选择", "tariffOption": "{{traffic}} GB,{{devices}} 台设备", "durationDays": "时长(天)", - "noBonusDescription": "无奖励活动——仅用于跟踪点击和注册。", + "noBonusDescription": "无奖励活动:仅用于跟踪点击和注册。", "active": "启用", "cancel": "取消", "save": "保存", @@ -2210,7 +2210,7 @@ }, "users": { "title": "活动用户", - "campaignRegistrations": "{{name}} — {{count}} 次注册", + "campaignRegistrations": "{{name}}:{{count}} 次注册", "noUsers": "没有注册用户", "user": "用户", "bonus": "奖励", @@ -3632,7 +3632,7 @@ "summaryErrors": "{{count}} 错误" }, "errors": { - "title": "{{count}} 个错误 — 显示详情" + "title": "{{count}} 个错误。显示详情" }, "filters": { "search": "按ID、用户名、邮箱搜索",