Convert i18n resource to key type

This commit is contained in:
Hoàng Rio 2018-11-09 13:51:28 +07:00
parent beab9a1be0
commit d47a23269d
No known key found for this signature in database
GPG Key ID: B19526CDE69535A1
22 changed files with 389 additions and 366 deletions

View File

@ -15,116 +15,120 @@ export default {
homepage: 'Homepage',
report_an_issue: 'Report an issue',
// Dashboard
enabled_protection:'Enable protection',
disabled_protection: 'Disable protection',
enable_protection: 'Enable protection',
enabled_protection: 'Enableed protection',
disable_protection: 'Disable protection',
disabled_protection: 'Disabled protection',
refresh_statics: 'Refresh statistics',
dns_query: 'DNS Queries',
'Blocked by': 'Chặn bởi',
'Blocked malware/phishing': 'Mã độc/lừa đảo đã chặn',
'Blocked adult websites': 'Website người lớn đã chặn',
'Top queried domains': 'Tên miền truy vấn nhiều',
'for the last 24 hours': 'trong 24 giờ qua',
'No domains found': 'Không có tên miền nào',
'Requests count': 'Số lần yêu cầu',
'Top blocked domains': 'Tên miền chặn nhiều',
'Top clients': 'Client dùng nhiều',
'No clients found': 'Không có client nào',
'General statistics': 'Thống kê chung',
'A number of DNS quieries processed for the last 24 hours': 'Số yêu cầu DNS đã xử lý trong 24 giờ qua',
'A number of DNS requests blocked by adblock filters and hosts blocklists': 'Số yêu cầu DNS bị chặn bởi bộ lọc quảng cáo và danh sách chặn host',
'A number of DNS requests blocked by the AdGuard browsing security module': 'Số yêu cầu DNS bị chặn bởi chế độ bảo vệ duyệt web AdGuard',
'A number of adult websites blocked': 'Số website người lớn đã chặn',
'Enforced safe search': 'Tìm kiếm an toàn',
'A number of DNS requests to search engines for which Safe Search was enforced': 'Số yêu cầu DNS tới công cụ tìm kiếm đã chuyển thành tìm kiếm an toàn',
'Average processing time': 'Thời gian xử lý trung bình',
'Average time in milliseconds on processing a DNS request': 'Thời gian trung bình cho một yêu cầu DNS tính bằng mili giây',
blocked_by: 'Blocked by',
stats_malware_phishing: 'Blocked malware/phishing',
stats_adult: 'Blocked adult websites',
stats_query_domain: 'Top queried domains',
for_last_24_hours: 'for the last 24 hours',
no_domains_found: 'No domains found',
requests_count: 'Requests count',
top_blocked_domains: 'Top blocked domains',
top_clients: 'Top clients',
no_clients_found: 'No clients found',
general_statistics: 'General statistics',
number_of_dns_query_24_hours: 'A number of DNS quieries processed for the last 24 hours',
number_of_dns_query_blocked_24_hours: 'A number of DNS requests blocked by adblock filters and hosts blocklists',
number_of_dns_query_blocked_24_hours_by_sec: 'A number of DNS requests blocked by the AdGuard browsing security module',
number_of_dns_query_blocked_24_hours_adult: 'A number of adult websites blocked',
enforced_save_search: 'Enforced safe search',
number_of_dns_query_to_safe_search: 'A number of DNS requests to search engines for which Safe Search was enforced',
average_processing_time: 'Average processing time',
average_processing_time_hint: 'Average time in milliseconds on processing a DNS request',
// Settings
'Block domains using filters and hosts files': 'Chặn tên miền sử dụng các bộ lọc và file hosts',
'You can setup blocking rules in the <a href="#filters">Filters</a> settings.': 'Bạn có thể thiết lập quy tắc chặn tại cài đặt <a href="#filters">Bộ lọc</a>.',
'Use AdGuard browsing security web service': 'Sử dụng dịch vụ bảo vệ duyệt web AdGuard',
'AdGuard Home will check if domain is blacklisted by the browsing security web service. It will use privacy-friendly lookup API to perform the check: only a short prefix of the domain name SHA256 hash is sent to the server.': 'AdGuard Home sẽ kiểm tra tên miền với dịch vụ bảo vệ duyệt web. Tính năng sử dụng một API thân thiện với quyền riêng tư: chỉ một phần ngắn tiền tố mã băm SHA256 được gửi đến máy chủ',
'Use AdGuard parental control web service': 'Sử dụng dịch vụ quản lý của phụ huynh AdGuard',
'AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.': 'AdGuard Home sẽ kiểm tra nếu tên miền chứa từ khoá người lớn. Tính năng sử dụng API thân thiện với quyền riêng tư tương tự với dịch vụ bảo vệ duyệt web',
'Enforce safe search': 'Bắt buộc tìm kiếm an toàn',
'AdGuard Home can enforce safe search in the following search engines: Google, Bing, Yandex.': 'AdGuard Home có thể bắt buộc tìm kiếm an toàn với các dịch vụ tìm kiếm: Google, Bing, Yandex.',
'No servers specified': 'Không có máy chủ nào được liệt kê',
'No settings': 'Không có cài đặt nào',
'General settings': 'Cài đặt chung',
'Upstream DNS servers': 'Máy chủ DNS tìm kiếm',
'If you keep this field empty, AdGuard Home will use <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> as an upstream. Use tls:// prefix for DNS over TLS servers.': 'Nếu bạn để trống mục này, AdGuard Home sẽ sử dụng <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> để tìm kiếm. Sử dụng tiền tố tls:// cho các máy chủ DNS dựa trên TLS.',
'Test upstreams': 'Kiểm tra',
Apply: 'Áp dụng',
block_domain_use_filters_and_hosts: 'Block domains using filters and hosts files',
filters_block_toggle_hint: 'You can setup blocking rules in the <a href="#filters">Filters</a> settings.',
use_adguard_browsing_sec: 'Use AdGuard browsing security web service',
use_adguard_browsing_sec_hint: 'AdGuard Home will check if domain is blacklisted by the browsing security web service. It will use privacy-friendly lookup API to perform the check: only a short prefix of the domain name SHA256 hash is sent to the server.',
use_adguard_parental: 'Use AdGuard parental control web service',
use_adguard_parental_hint: 'AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.',
enforce_safe_search: 'Enforce safe search',
enforce_save_search_hint: 'AdGuard Home can enforce safe search in the following search engines: Google, Bing, Yandex.',
no_servers_specified: 'No servers specified',
no_settings: 'No settings',
general_settings: 'General settings',
upstream_dns: 'Upstream DNS servers',
upstream_dns_hint: 'If you keep this field empty, AdGuard Home will use <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> as an upstream. Use tls:// prefix for DNS over TLS servers.',
test_upstream_btn: 'Test upstreams',
apply_btn: 'Apply',
// Settings Toasts
'Disabled filtering': 'Đã tắt chặn quảng cáo',
'Enabled filtering': 'Đã bật chặn quảng cáo',
'Disabled safebrowsing': 'Đã tắt bảo vệ duyệt web',
'Enabled safebrowsing': 'Đã bật bảo vệ duyệt web',
'Disabled parental control': 'Đã tắt quản lý của phụ huynh',
'Enabled parental control': 'Đã bật quản lý của phụ huynh',
'Disabled safe search': 'Đã tắt tìm kiếm an toàn',
'Enabled safe search': 'Đã bật tìm kiếm an toàn',
disabled_filtering_toast: 'Disabled filtering',
enabled_filtering_toast: 'Enabled filtering',
disabled_safe_browsing_toast: 'Disabled safebrowsing',
enabled_safe_browsing_toast: 'Enabled safebrowsing',
disabled_parental_toast: 'Disabled parental control',
enabled_parental_toast: 'Enabled parental control',
disabled_safe_search_toast: 'Disabled safe search',
enabled_save_search_toast: 'Enabled safe search',
// Filters
Enabled: 'Kích hoạt',
Name: 'Tên',
'Filter URL': 'URL bộ lọc',
'Rules count': 'Số quy tắc',
'Last time updated': 'Cập nhật cuối',
Actions: 'Thao tác',
Delete: 'Xoá',
'Filters and hosts blocklists': 'Danh sách bộ lọc và hosts',
'AdGuard Home understands basic adblock rules and hosts files syntax.': 'AdGuard home hiểu các quy tắc chặn quảng cáo đơn giản và cú pháp file hosts',
'No filters added': 'Không có bộ lọc nào được thêm',
'Add filter': 'Thêm bộ lọc',
Cancel: 'Huỷ',
'Enter name': 'Nhập tên',
'Enter URL': 'Nhập URL',
'Check updates': 'Kiểm tra cập nhật',
'New filter subscription': 'Đăng ký bộ lọc mới',
'Enter a valid URL to a filter subscription or a hosts file.': 'Nhập URL hợp lệ của bộ lọc hoặc file hosts',
'Custom filtering rules': 'Quy tắc lọc tuỳ chỉnh',
'Enter one rule on a line. You can use either adblock rules or hosts files syntax.': 'Nhập mỗi quy tắc 1 dòng. Có thể sử dụng quy tắc chặn quảng cáo hoặc cú pháp file host',
Examples: 'Ví dụ',
'block access to the example.org domain and all its subdomains': 'Chặn truy cập tới tên miền example.org và tất cả tên miền con',
'unblock access to the example.org domain and all its subdomains': 'Không chặn truy cập tới tên miền example.org và tất cả tên miền con',
'AdGuard Home will now return 127.0.0.1 address for the example.org domain (but not its subdomains).': 'AdGuard Home sẽ phản hồi địa chỉ IP 127.0.0.1 cho tên miền example.org (không áp dụng tên miền con)',
'! Here goes a comment': '! Đây là một chú thích',
'just a comment': 'Chỉ là một chú thích',
'# Also a comment': '# Cũng là một chú thích',
'All filters are already up-to-date': 'Tất cả bộ lọc đã được cập nhật',
'Updated the upstream DNS servers': 'Đã cập nhật máy chủ DNS tìm kiếm',
'Specified DNS servers are working correctly': 'Máy chủ DNS có thể sử dụng',
'Server "{{key}}": could not be used, please check that you\'ve written it correctly': 'Máy chủ "{{key}}": không thể sử dụng, vui lòng kiểm tra bạn đã điền chính xác',
enabled_table_header: 'Enabled',
name_table_header: 'Name',
filter_url_table_header: 'Filter URL',
rules_count_table_header: 'Rules count',
last_time_updated_table_header: 'Last time updated',
actions_table_header: 'Actions',
delete_table_action: 'Delete',
filters_and_hosts: 'Filters and hosts blocklists',
filters_and_hosts_hint: 'AdGuard Home understands basic adblock rules and hosts files syntax.',
no_filters_added: 'No filters added',
add_filter_btn: 'Add filter',
cancel_btn: 'Cancel',
enter_name_hint: 'Enter name',
enter_url_hint: 'Enter URL',
check_updates_btn: 'Check updates',
new_filter_btn: 'New filter subscription',
enter_valid_filter_url: 'Enter a valid URL to a filter subscription or a hosts file.',
custom_filter_rules: 'Custom filtering rules',
custom_filter_rules_hint: 'Enter one rule on a line. You can use either adblock rules or hosts files syntax.',
examples_title: 'Examples',
example_meaning_filter_block: 'block access to the example.org domain and all its subdomains',
example_meaning_filter_whitelist: 'unblock access to the example.org domain and all its subdomains',
example_meaning_host_block: 'AdGuard Home will now return 127.0.0.1 address for the example.org domain (but not its subdomains).',
example_comment: '! Here goes a comment',
example_comment_meaning: 'just a comment',
example_comment_hash: '# Also a comment',
all_filters_up_to_date_toast: 'All filters are already up-to-date',
updated_upstream_dns_toast: 'Updated the upstream DNS servers',
dns_test_ok_toast: 'Specified DNS servers are working correctly',
dns_test_not_ok_toast: 'Server "{{key}}": could not be used, please check that you\'ve written it correctly',
// Logs
Unblock: 'Bỏ chặn',
Block: 'Chặn',
Time: 'Thời gian',
'Domain name': 'Tên miền',
Type: 'Loại',
Response: 'Phản hồi',
Empty: 'Rỗng',
'Show all': 'Hiện tất cả',
'Show filtered': 'Chỉ hiện đã chặn',
'No logs found': 'Không có lịch sử truy vấn',
'Disable log': 'Tắt lịch sử truy vấn',
'Download log file': 'Tải tập tin lịch sử truy vấn',
Refresh: 'Làm mới',
'Enable log': 'Bật lịch sử truy vấn',
'Last 5000 DNS queries': '5000 truy vấn DNS gần nhất',
Previous: 'Trang trước',
Next: 'Trang sau',
'Loading...': 'Đang tải...',
Page: 'Trang',
of: 'của',
rows: 'hàng',
'Updated the custom filtering rules': 'Đã cập nhật quy tắc lọc tuỳ chỉnh',
'Rule removed from the custom filtering rules': 'Quy tắc đã được xoá khỏi quy tắc lọc tuỳ chỉnh',
'Rule added to the custom filtering rules': 'Quy tắc đã được thêm vào quy tắc lọc tuỳ chỉnh',
ublock_btn: 'Unblock',
block_btn: 'Block',
time_table_header: 'Time',
domain_name_table_header: 'Domain name',
type_table_header: 'Type',
response_table_header: 'Response',
empty_response_status: 'Empty',
show_all_filter_type: 'Show all',
show_filtered_type: 'Show filtered',
no_logs_found: 'No logs found',
disabled_log_btn: 'Disable log',
download_log_file_btn: 'Download log file',
refresh_btn: 'Refresh',
enabled_log_btn: 'Enable log',
last_5000_dns_queries: 'Last 5000 DNS queries',
previous_btn: 'Previous',
next_btn: 'Next',
loading_table_status: 'Loading...',
page_table_footer_text: 'Page',
of_table_footer_text: 'of',
rows_table_footer_text: 'rows',
updated_custom_filtering_toast: 'Updated the custom filtering rules',
rule_removed_from_custom_filtering_toast: 'Rule removed from the custom filtering rules',
rule_added_to_custom_filtering_toast: 'Rule added to the custom filtering rules',
query_log_disabled_toast: 'Query log disabled',
query_log_enabled_toast: 'Query log enabled',
// Popover
Source: 'Nguồn',
'Found in the known domains database.': 'Tìm thấy trong cơ sở dữ liệu tên miền',
Category: 'Thể loại',
source_label: 'Source',
found_in_known_domain_db: 'Found in the known domains database.',
category_label: 'Category',
// Popover filter
Rule: 'Quy tắc',
Filter: 'Bộ lọc',
rule_label: 'Rule',
filter_label: 'Filter',
},
}
};

View File

@ -1,132 +1,134 @@
export default {
translation: {
// Header
Back: 'Quay lại',
Dashboard: 'Tổng quan',
Settings: 'Cài đặt',
Filters: 'Bộ lọc',
'Query Log': 'Lịch sử truy vấn',
FAQ: 'Hỏi đáp',
back: 'Quay lại',
dashboard: 'Tổng quan',
settings: 'Cài đặt',
filters: 'Bộ lọc',
query_log: 'Lịch sử truy vấn',
faq: 'Hỏi đáp',
version: 'phiên bản',
address: 'địa chỉ',
ON: 'Đang bật',
OFF: 'Đang tắt',
on: 'Đang bật',
off: 'Đang tắt',
// Footer
Homepage: 'Trang chủ',
'Report an issue': 'Báo lỗi',
homepage: 'Trang chủ',
report_an_issue: 'Báo lỗi',
// Dashboard
'Enable protection': 'Bật bảo vệ',
'Disable protection': 'Tắt bảo vệ',
'Refresh statistics': 'Làm mới thống kê',
'DNS Queries': 'Truy vấn DNS',
'Blocked by': 'Chặn bởi',
'Blocked malware/phishing': 'Mã độc/lừa đảo đã chặn',
'Blocked adult websites': 'Website người lớn đã chặn',
'Top queried domains': 'Tên miền truy vấn nhiều',
'for the last 24 hours': 'trong 24 giờ qua',
'No domains found': 'Không có tên miền nào',
'Requests count': 'Số lần yêu cầu',
'Top blocked domains': 'Tên miền chặn nhiều',
'Top clients': 'Client dùng nhiều',
'No clients found': 'Không có client nào',
'General statistics': 'Thống kê chung',
'A number of DNS quieries processed for the last 24 hours': 'Số yêu cầu DNS đã xử lý trong 24 giờ qua',
'A number of DNS requests blocked by adblock filters and hosts blocklists': 'Số yêu cầu DNS bị chặn bởi bộ lọc quảng cáo và danh sách chặn host',
'A number of DNS requests blocked by the AdGuard browsing security module': 'Số yêu cầu DNS bị chặn bởi chế độ bảo vệ duyệt web AdGuard',
'A number of adult websites blocked': 'Số website người lớn đã chặn',
'Enforced safe search': 'Tìm kiếm an toàn',
'A number of DNS requests to search engines for which Safe Search was enforced': 'Số yêu cầu DNS tới công cụ tìm kiếm đã chuyển thành tìm kiếm an toàn',
'Average processing time': 'Thời gian xử lý trung bình',
'Average time in milliseconds on processing a DNS request': 'Thời gian trung bình cho một yêu cầu DNS tính bằng mili giây',
enable_protection: 'Bật bảo vệ',
enabled_protection: 'Đã bật bảo vệ',
disable_protection: 'Tắt bảo vệ',
disabled_protection: 'Đã tắt bảo vệ',
refresh_statics: 'Làm mới thống kê',
dns_query: 'Truy vấn DNS',
blocked_by: 'Chặn bởi',
stats_malware_phishing: 'Mã độc/lừa đảo đã chặn',
stats_adult: 'Website người lớn đã chặn',
stats_query_domain: 'Tên miền truy vấn nhiều',
for_last_24_hours: 'trong 24 giờ qua',
no_domains_found: 'Không có tên miền nào',
requests_count: 'Số lần yêu cầu',
top_blocked_domains: 'Tên miền chặn nhiều',
top_clients: 'Client dùng nhiều',
no_clients_found: 'Không có client nào',
general_statistics: 'Thống kê chung',
number_of_dns_query_24_hours: 'Số yêu cầu DNS đã xử lý trong 24 giờ qua',
number_of_dns_query_blocked_24_hours: 'Số yêu cầu DNS bị chặn bởi bộ lọc quảng cáo và danh sách chặn host',
number_of_dns_query_blocked_24_hours_by_sec: 'Số yêu cầu DNS bị chặn bởi chế độ bảo vệ duyệt web AdGuard',
number_of_dns_query_blocked_24_hours_adult: 'Số website người lớn đã chặn',
enforced_save_search: 'Tìm kiếm an toàn',
number_of_dns_query_to_safe_search: 'Số yêu cầu DNS tới công cụ tìm kiếm đã chuyển thành tìm kiếm an toàn',
average_processing_time: 'Thời gian xử lý trung bình',
average_processing_time_hint: 'Thời gian trung bình cho một yêu cầu DNS tính bằng mili giây',
// Settings
'Block domains using filters and hosts files': 'Chặn tên miền sử dụng các bộ lọc và file hosts',
'You can setup blocking rules in the <a href="#filters">Filters</a> settings.': 'Bạn có thể thiết lập quy tắc chặn tại cài đặt <a href="#filters">Bộ lọc</a>.',
'Use AdGuard browsing security web service': 'Sử dụng dịch vụ bảo vệ duyệt web AdGuard',
'AdGuard Home will check if domain is blacklisted by the browsing security web service. It will use privacy-friendly lookup API to perform the check: only a short prefix of the domain name SHA256 hash is sent to the server.': 'AdGuard Home sẽ kiểm tra tên miền với dịch vụ bảo vệ duyệt web. Tính năng sử dụng một API thân thiện với quyền riêng tư: chỉ một phần ngắn tiền tố mã băm SHA256 được gửi đến máy chủ',
'Use AdGuard parental control web service': 'Sử dụng dịch vụ quản lý của phụ huynh AdGuard',
'AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.': 'AdGuard Home sẽ kiểm tra nếu tên miền chứa từ khoá người lớn. Tính năng sử dụng API thân thiện với quyền riêng tư tương tự với dịch vụ bảo vệ duyệt web',
'Enforce safe search': 'Bắt buộc tìm kiếm an toàn',
'AdGuard Home can enforce safe search in the following search engines: Google, Bing, Yandex.': 'AdGuard Home có thể bắt buộc tìm kiếm an toàn với các dịch vụ tìm kiếm: Google, Bing, Yandex.',
'No servers specified': 'Không có máy chủ nào được liệt kê',
'No settings': 'Không có cài đặt nào',
'General settings': 'Cài đặt chung',
'Upstream DNS servers': 'Máy chủ DNS tìm kiếm',
'If you keep this field empty, AdGuard Home will use <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> as an upstream. Use tls:// prefix for DNS over TLS servers.': 'Nếu bạn để trống mục này, AdGuard Home sẽ sử dụng <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> để tìm kiếm. Sử dụng tiền tố tls:// cho các máy chủ DNS dựa trên TLS.',
'Test upstreams': 'Kiểm tra',
Apply: 'Áp dụng',
block_domain_use_filters_and_hosts: 'Chặn tên miền sử dụng các bộ lọc và file hosts',
filters_block_toggle_hint: 'Bạn có thể thiết lập quy tắc chặn tại cài đặt <a href="#filters">Bộ lọc</a>.',
use_adguard_browsing_sec: 'Sử dụng dịch vụ bảo vệ duyệt web AdGuard',
use_adguard_browsing_sec_hint: 'AdGuard Home sẽ kiểm tra tên miền với dịch vụ bảo vệ duyệt web. Tính năng sử dụng một API thân thiện với quyền riêng tư: chỉ một phần ngắn tiền tố mã băm SHA256 được gửi đến máy chủ',
use_adguard_parental: 'Sử dụng dịch vụ quản lý của phụ huynh AdGuard',
use_adguard_parental_hint: 'AdGuard Home sẽ kiểm tra nếu tên miền chứa từ khoá người lớn. Tính năng sử dụng API thân thiện với quyền riêng tư tương tự với dịch vụ bảo vệ duyệt web',
enforce_safe_search: 'Bắt buộc tìm kiếm an toàn',
enforce_save_search_hint: 'AdGuard Home có thể bắt buộc tìm kiếm an toàn với các dịch vụ tìm kiếm: Google, Bing, Yandex.',
no_servers_specified: 'Không có máy chủ nào được liệt kê',
no_settings: 'Không có cài đặt nào',
general_settings: 'Cài đặt chung',
upstream_dns: 'Máy chủ DNS tìm kiếm',
upstream_dns_hint: 'Nếu bạn để trống mục này, AdGuard Home sẽ sử dụng <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> để tìm kiếm. Sử dụng tiền tố tls:// cho các máy chủ DNS dựa trên TLS.',
test_upstream_btn: 'Kiểm tra',
apply_btn: 'Áp dụng',
// Settings Toasts
'Disabled filtering': 'Đã tắt chặn quảng cáo',
'Enabled filtering': 'Đã bật chặn quảng cáo',
'Disabled safebrowsing': 'Đã tắt bảo vệ duyệt web',
'Enabled safebrowsing': 'Đã bật bảo vệ duyệt web',
'Disabled parental control': 'Đã tắt quản lý của phụ huynh',
'Enabled parental control': 'Đã bật quản lý của phụ huynh',
'Disabled safe search': 'Đã tắt tìm kiếm an toàn',
'Enabled safe search': 'Đã bật tìm kiếm an toàn',
disabled_filtering_toast: 'Đã tắt chặn quảng cáo',
enabled_filtering_toast: 'Đã bật chặn quảng cáo',
disabled_safe_browsing_toast: 'Đã tắt bảo vệ duyệt web',
enabled_safe_browsing_toast: 'Đã bật bảo vệ duyệt web',
disabled_parental_toast: 'Đã tắt quản lý của phụ huynh',
enabled_parental_toast: 'Đã bật quản lý của phụ huynh',
disabled_safe_search_toast: 'Đã tắt tìm kiếm an toàn',
enabled_save_search_toast: 'Đã bật tìm kiếm an toàn',
// Filters
Enabled: 'Kích hoạt',
Name: 'Tên',
'Filter URL': 'URL bộ lọc',
'Rules count': 'Số quy tắc',
'Last time updated': 'Cập nhật cuối',
Actions: 'Thao tác',
Delete: 'Xoá',
'Filters and hosts blocklists': 'Danh sách bộ lọc và hosts',
'AdGuard Home understands basic adblock rules and hosts files syntax.': 'AdGuard home hiểu các quy tắc chặn quảng cáo đơn giản và cú pháp file hosts',
'No filters added': 'Không có bộ lọc nào được thêm',
'Add filter': 'Thêm bộ lọc',
Cancel: 'Huỷ',
'Enter name': 'Nhập tên',
'Enter URL': 'Nhập URL',
'Check updates': 'Kiểm tra cập nhật',
'New filter subscription': 'Đăng ký bộ lọc mới',
'Enter a valid URL to a filter subscription or a hosts file.': 'Nhập URL hợp lệ của bộ lọc hoặc file hosts',
'Custom filtering rules': 'Quy tắc lọc tuỳ chỉnh',
'Enter one rule on a line. You can use either adblock rules or hosts files syntax.': 'Nhập mỗi quy tắc 1 dòng. Có thể sử dụng quy tắc chặn quảng cáo hoặc cú pháp file host',
Examples: 'Ví dụ',
'block access to the example.org domain and all its subdomains': 'Chặn truy cập tới tên miền example.org và tất cả tên miền con',
'unblock access to the example.org domain and all its subdomains': 'Không chặn truy cập tới tên miền example.org và tất cả tên miền con',
'AdGuard Home will now return 127.0.0.1 address for the example.org domain (but not its subdomains).': 'AdGuard Home sẽ phản hồi địa chỉ IP 127.0.0.1 cho tên miền example.org (không áp dụng tên miền con)',
'! Here goes a comment': '! Đây là một chú thích',
'just a comment': 'Chỉ là một chú thích',
'# Also a comment': '# Cũng là một chú thích',
'All filters are already up-to-date': 'Tất cả bộ lọc đã được cập nhật',
'Updated the upstream DNS servers': 'Đã cập nhật máy chủ DNS tìm kiếm',
'Specified DNS servers are working correctly': 'Máy chủ DNS có thể sử dụng',
'Server "{{key}}": could not be used, please check that you\'ve written it correctly': 'Máy chủ "{{key}}": không thể sử dụng, vui lòng kiểm tra bạn đã điền chính xác',
enabled_table_header: 'Kích hoạt',
name_table_header: 'Tên',
filter_url_table_header: 'URL bộ lọc',
rules_count_table_header: 'Số quy tắc',
last_time_updated_table_header: 'Cập nhật cuối',
actions_table_header: 'Thao tác',
delete_table_action: 'Xoá',
filters_and_hosts: 'Danh sách bộ lọc và hosts',
filters_and_hosts_hint: 'AdGuard home hiểu các quy tắc chặn quảng cáo đơn giản và cú pháp file hosts',
no_filters_added: 'Không có bộ lọc nào được thêm',
add_filter_btn: 'Thêm bộ lọc',
cancel_btn: 'Huỷ',
enter_name_hint: 'Nhập tên',
enter_url_hint: 'Nhập URL',
check_updates_btn: 'Kiểm tra cập nhật',
new_filter_btn: 'Đăng ký bộ lọc mới',
enter_valid_filter_url: 'Nhập URL hợp lệ của bộ lọc hoặc file hosts',
custom_filter_rules: 'Quy tắc lọc tuỳ chỉnh',
custom_filter_rules_hint: 'Nhập mỗi quy tắc 1 dòng. Có thể sử dụng quy tắc chặn quảng cáo hoặc cú pháp file host',
examples_title: 'Ví dụ',
example_meaning_filter_block: 'Chặn truy cập tới tên miền example.org và tất cả tên miền con',
example_meaning_filter_whitelist: 'Không chặn truy cập tới tên miền example.org và tất cả tên miền con',
example_meaning_host_block: 'AdGuard Home sẽ phản hồi địa chỉ IP 127.0.0.1 cho tên miền example.org (không áp dụng tên miền con)',
example_comment: '! Đây là một chú thích',
example_comment_meaning: 'Chỉ là một chú thích',
example_comment_hash: '# Cũng là một chú thích',
all_filters_up_to_date_toast: 'Tất cả bộ lọc đã được cập nhật',
updated_upstream_dns_toast: 'Đã cập nhật máy chủ DNS tìm kiếm',
dns_test_ok_toast: 'Máy chủ DNS có thể sử dụng',
dns_test_not_ok_toast: 'Máy chủ "{{key}}": không thể sử dụng, vui lòng kiểm tra bạn đã điền chính xác',
// Logs
Unblock: 'Bỏ chặn',
Block: 'Chặn',
Time: 'Thời gian',
'Domain name': 'Tên miền',
Type: 'Loại',
Response: 'Phản hồi',
Empty: 'Rỗng',
'Show all': 'Hiện tất cả',
'Show filtered': 'Chỉ hiện đã chặn',
'No logs found': 'Không có lịch sử truy vấn',
'Disable log': 'Tắt lịch sử truy vấn',
'Download log file': 'Tải tập tin lịch sử truy vấn',
Refresh: 'Làm mới',
'Enable log': 'Bật lịch sử truy vấn',
'Last 5000 DNS queries': '5000 truy vấn DNS gần nhất',
Previous: 'Trang trước',
Next: 'Trang sau',
'Loading...': 'Đang tải...',
Page: 'Trang',
of: 'của',
rows: 'hàng',
'Updated the custom filtering rules': 'Đã cập nhật quy tắc lọc tuỳ chỉnh',
'Rule removed from the custom filtering rules': 'Quy tắc đã được xoá khỏi quy tắc lọc tuỳ chỉnh',
'Rule added to the custom filtering rules': 'Quy tắc đã được thêm vào quy tắc lọc tuỳ chỉnh',
'Query log disabled': 'Đã bật lịch sử truy vấn',
'Query log enabled': 'Đã tắt lịch sử truy vấn',
ublock_btn: 'Bỏ chặn',
block_btn: 'Chặn',
time_table_header: 'Thời gian',
domain_name_table_header: 'Tên miền',
type_table_header: 'Loại',
response_table_header: 'Phản hồi',
empty_response_status: 'Rỗng',
show_all_filter_type: 'Hiện tất cả',
show_filtered_type: 'Chỉ hiện đã chặn',
no_logs_found: 'Không có lịch sử truy vấn',
disabled_log_btn: 'Tắt lịch sử truy vấn',
download_log_file_btn: 'Tải tập tin lịch sử truy vấn',
refresh_btn: 'Làm mới',
enabled_log_btn: 'Bật lịch sử truy vấn',
last_5000_dns_queries: '5000 truy vấn DNS gần nhất',
previous_btn: 'Trang trước',
next_btn: 'Trang sau',
loading_table_status: 'Đang tải...',
page_table_footer_text: 'Trang',
of_table_footer_text: 'của',
rows_table_footer_text: 'hàng',
updated_custom_filtering_toast: 'Đã cập nhật quy tắc lọc tuỳ chỉnh',
rule_removed_from_custom_filtering_toast: 'Quy tắc đã được xoá khỏi quy tắc lọc tuỳ chỉnh',
rule_added_to_custom_filtering_toast: 'Quy tắc đã được thêm vào quy tắc lọc tuỳ chỉnh',
query_log_disabled_toast: 'Đã tắt lịch sử truy vấn',
query_log_enabled_toast: 'Đã bật lịch sử truy vấn',
// Popover
Source: 'Nguồn',
'Found in the known domains database.': 'Tìm thấy trong cơ sở dữ liệu tên miền',
Category: 'Thể loại',
source_label: 'Nguồn',
found_in_known_domain_db: 'Tìm thấy trong cơ sở dữ liệu tên miền',
category_label: 'Thể loại',
// Popover filter
Rule: 'Quy tắc',
Filter: 'Bộ lọc',
rule_label: 'Quy tắc',
filter_label: 'Bộ lọc',
},
};

View File

@ -22,40 +22,40 @@ export const toggleSetting = (settingKey, status) => async (dispatch) => {
switch (settingKey) {
case 'filtering':
if (status) {
successMessage = 'Disabled filtering';
successMessage = 'disabled_filtering_toast';
await apiClient.disableFiltering();
} else {
successMessage = 'Enabled filtering';
successMessage = 'enabled_filtering_toast';
await apiClient.enableFiltering();
}
dispatch(toggleSettingStatus({ settingKey }));
break;
case 'safebrowsing':
if (status) {
successMessage = 'Disabled safebrowsing';
successMessage = 'disabled_safe_browsing_toast';
await apiClient.disableSafebrowsing();
} else {
successMessage = 'Enabled safebrowsing';
successMessage = 'enabled_safe_browsing_toast';
await apiClient.enableSafebrowsing();
}
dispatch(toggleSettingStatus({ settingKey }));
break;
case 'parental':
if (status) {
successMessage = 'Disabled parental control';
successMessage = 'disabled_parental_toast';
await apiClient.disableParentalControl();
} else {
successMessage = 'Enabled parental control';
successMessage = 'enabled_parental_toast';
await apiClient.enableParentalControl();
}
dispatch(toggleSettingStatus({ settingKey }));
break;
case 'safesearch':
if (status) {
successMessage = 'Disabled safe search';
successMessage = 'disabled_safe_search_toast';
await apiClient.disableSafesearch();
} else {
successMessage = 'Enabled safe search';
successMessage = 'enabled_save_search_toast';
await apiClient.enableSafesearch();
}
dispatch(toggleSettingStatus({ settingKey }));
@ -124,10 +124,10 @@ export const toggleProtection = status => async (dispatch) => {
try {
if (status) {
successMessage = 'Disabled protection';
successMessage = 'disabled_protection';
await apiClient.disableGlobalProtection();
} else {
successMessage = 'Enabled protection';
successMessage = 'enabled_protection';
await apiClient.enableGlobalProtection();
}
@ -272,14 +272,14 @@ export const toggleLogStatus = queryLogEnabled => async (dispatch) => {
let successMessage;
if (queryLogEnabled) {
toggleMethod = apiClient.disableQueryLog.bind(apiClient);
successMessage = 'disabled';
successMessage = 'query_log_disabled_toast';
} else {
toggleMethod = apiClient.enableQueryLog.bind(apiClient);
successMessage = 'enabled';
successMessage = 'query_log_enabled_toast';
}
try {
await toggleMethod();
dispatch(addSuccessToast(`Query log ${successMessage}`));
dispatch(addSuccessToast(successMessage));
dispatch(toggleLogStatusSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
@ -298,7 +298,7 @@ export const setRules = rules => async (dispatch) => {
.replace(/^\n/g, '')
.replace(/\n\s*\n/g, '\n');
await apiClient.setRules(replacedLineEndings);
dispatch(addSuccessToast('Updated the custom filtering rules'));
dispatch(addSuccessToast('updated_custom_filtering_toast'));
dispatch(setRulesSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
@ -360,7 +360,7 @@ export const refreshFilters = () => async (dispatch) => {
if (refreshText.includes('OK')) {
if (refreshText.includes('OK 0')) {
dispatch(addSuccessToast('All filters are already up-to-date'));
dispatch(addSuccessToast('all_filters_up_to_date_toast'));
} else {
dispatch(addSuccessToast(refreshText.replace(/OK /g, '')));
}
@ -457,7 +457,7 @@ export const setUpstream = url => async (dispatch) => {
dispatch(setUpstreamRequest());
try {
await apiClient.setUpstream(url);
dispatch(addSuccessToast('Updated the upstream DNS servers'));
dispatch(addSuccessToast('updated_upstream_dns_toast'));
dispatch(setUpstreamSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
@ -477,13 +477,13 @@ export const testUpstream = servers => async (dispatch) => {
const testMessages = Object.keys(upstreamResponse).map((key) => {
const message = upstreamResponse[key];
if (message !== 'OK') {
dispatch(addErrorToast({ error: t('Server "{{key}}": could not be used, please check that you\'ve written it correctly', { key }) }));
dispatch(addErrorToast({ error: t('dns_test_not_ok_toast', { key }) }));
}
return message;
});
if (testMessages.every(message => message === 'OK')) {
dispatch(addSuccessToast('Specified DNS servers are working correctly'));
dispatch(addSuccessToast('dns_test_ok_toast'));
}
dispatch(testUpstreamSuccess());

View File

@ -30,7 +30,7 @@ class BlockedDomains extends Component {
);
},
}, {
Header: <Trans>Requests count</Trans>,
Header: <Trans>requests_count</Trans>,
accessor: 'domain',
maxWidth: 190,
Cell: ({ value }) => {
@ -51,14 +51,14 @@ class BlockedDomains extends Component {
render() {
const { t } = this.props;
return (
<Card title={ t('Top blocked domains') } subtitle={ t('for the last 24 hours') } bodyType="card-table" refresh={this.props.refreshButton}>
<Card title={ t('top_blocked_domains') } subtitle={ t('for_last_24_hours') } bodyType="card-table" refresh={this.props.refreshButton}>
<ReactTable
data={map(this.props.topBlockedDomains, (value, prop) => (
{ ip: prop, domain: value }
))}
columns={this.columns}
showPagination={false}
noDataText={ t('No domains found') }
noDataText={ t('no_domains_found') }
minRows={6}
className="-striped -highlight card-table-overflow stats__table"
/>

View File

@ -26,7 +26,7 @@ class Clients extends Component {
Cell: ({ value }) => (<div className="logs__row logs__row--overflow"><span className="logs__text" title={value}>{value}</span></div>),
sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
}, {
Header: <Trans>Requests count</Trans>,
Header: <Trans>requests_count</Trans>,
accessor: 'count',
Cell: ({ value }) => {
const percent = getPercent(this.props.dnsQueries, value);
@ -41,14 +41,14 @@ class Clients extends Component {
render() {
const { t } = this.props;
return (
<Card title={ t('Top clients') } subtitle={ t('for the last 24 hours') } bodyType="card-table" refresh={this.props.refreshButton}>
<Card title={ t('top_clients') } subtitle={ t('for_last_24_hours') } bodyType="card-table" refresh={this.props.refreshButton}>
<ReactTable
data={map(this.props.topClients, (value, prop) => (
{ ip: prop, count: value }
))}
columns={this.columns}
showPagination={false}
noDataText={ t('No clients found') }
noDataText={ t('no_clients_found') }
minRows={6}
className="-striped -highlight card-table-overflow"
/>

View File

@ -8,13 +8,13 @@ import Tooltip from '../ui/Tooltip';
const tooltipType = 'tooltip-custom--narrow';
const Counters = props => (
<Card title={ props.t('General statistics') } subtitle={ props.t('for the last 24 hours') } bodyType="card-table" refresh={props.refreshButton}>
<Card title={ props.t('general_statistics') } subtitle={ props.t('for_last_24_hours') } bodyType="card-table" refresh={props.refreshButton}>
<table className="table card-table">
<tbody>
<tr>
<td>
<Trans>DNS Queries</Trans>
<Tooltip text={ props.t('A number of DNS quieries processed for the last 24 hours') } type={tooltipType} />
<Trans>dns_query</Trans>
<Tooltip text={ props.t('number_of_dns_query_24_hours') } type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
@ -24,8 +24,8 @@ const Counters = props => (
</tr>
<tr>
<td>
<Trans>Blocked by</Trans> <a href="#filters"><Trans>Filters</Trans></a>
<Tooltip text={ props.t('A number of DNS requests blocked by adblock filters and hosts blocklists') } type={tooltipType} />
<Trans>blocked_by</Trans> <a href="#filters"><Trans>Filters</Trans></a>
<Tooltip text={ props.t('number_of_dns_query_blocked_24_hours') } type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
@ -35,8 +35,8 @@ const Counters = props => (
</tr>
<tr>
<td>
<Trans>Blocked malware/phishing</Trans>
<Tooltip text={ props.t('A number of DNS requests blocked by the AdGuard browsing security module') } type={tooltipType} />
<Trans>stats_malware_phishing</Trans>
<Tooltip text={ props.t('number_of_dns_query_blocked_24_hours_by_sec') } type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
@ -46,8 +46,8 @@ const Counters = props => (
</tr>
<tr>
<td>
<Trans>Blocked adult websites</Trans>
<Tooltip text={ props.t('A number of adult websites blocked') } type={tooltipType} />
<Trans>stats_adult</Trans>
<Tooltip text={ props.t('number_of_dns_query_blocked_24_hours_adult') } type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
@ -57,8 +57,8 @@ const Counters = props => (
</tr>
<tr>
<td>
<Trans>Enforced safe search</Trans>
<Tooltip text={ props.t('A number of DNS requests to search engines for which Safe Search was enforced') } type={tooltipType} />
<Trans>enforced_save_search</Trans>
<Tooltip text={ props.t('number_of_dns_query_to_safe_search') } type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
@ -68,8 +68,8 @@ const Counters = props => (
</tr>
<tr>
<td>
<Trans>Average processing time</Trans>
<Tooltip text={ props.t('Average time in milliseconds on processing a DNS request') } type={tooltipType} />
<Trans>average_processing_time</Trans>
<Tooltip text={ props.t('average_processing_time_hint') } type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">

View File

@ -39,7 +39,7 @@ class QueriedDomains extends Component {
);
},
}, {
Header: <Trans>Requests count</Trans>,
Header: <Trans>requests_count</Trans>,
accessor: 'count',
maxWidth: 190,
Cell: ({ value }) => {
@ -55,14 +55,14 @@ class QueriedDomains extends Component {
render() {
const { t } = this.props;
return (
<Card title={ t('Top queried domains') } subtitle={ t('for the last 24 hours') } bodyType="card-table" refresh={this.props.refreshButton}>
<Card title={ t('stats_query_domain') } subtitle={ t('for_last_24_hours') } bodyType="card-table" refresh={this.props.refreshButton}>
<ReactTable
data={map(this.props.topQueriedDomains, (value, prop) => (
{ ip: prop, count: value }
))}
columns={this.columns}
showPagination={false}
noDataText={ t('No domains found') }
noDataText={ t('no_domains_found') }
minRows={6}
className="-striped -highlight card-table-overflow stats__table"
/>

View File

@ -31,7 +31,7 @@ class Statistics extends Component {
{dnsQueries}
</div>
<div className="card-title-stats">
<Trans>DNS Queries</Trans>
<Trans>dns_query</Trans>
</div>
</div>
<div className="card-chart-bg">
@ -49,7 +49,7 @@ class Statistics extends Component {
{getPercent(dnsQueries, blockedFiltering)}
</div>
<div className="card-title-stats">
<Trans>Blocked by</Trans> <a href="#filters"><Trans>Filters</Trans></a>
<Trans>blocked_by</Trans> <a href="#filters"><Trans>Filters</Trans></a>
</div>
</div>
<div className="card-chart-bg">
@ -67,7 +67,7 @@ class Statistics extends Component {
{getPercent(dnsQueries, replacedSafebrowsing)}
</div>
<div className="card-title-stats">
<Trans>Blocked malware/phishing</Trans>
<Trans>stats_malware_phishing</Trans>
</div>
</div>
<div className="card-chart-bg">
@ -85,7 +85,7 @@ class Statistics extends Component {
{getPercent(dnsQueries, replacedParental)}
</div>
<div className="card-title-stats">
<Trans>Blocked adult websites</Trans>
<Trans>stats_adult</Trans>
</div>
</div>
<div className="card-chart-bg">

View File

@ -26,12 +26,12 @@ class Dashboard extends Component {
getToggleFilteringButton = () => {
const { protectionEnabled } = this.props.dashboard;
const buttonText = protectionEnabled ? 'Disable' : 'Enable';
const buttonText = protectionEnabled ? 'disable_protection' : 'enable_protection';
const buttonClass = protectionEnabled ? 'btn-gray' : 'btn-success';
return (
<button type="button" className={`btn btn-sm mr-2 ${buttonClass}`} onClick={() => this.props.toggleProtection(protectionEnabled)}>
<Trans>{buttonText} protection</Trans>
<Trans>{buttonText}</Trans>
</button>
);
}
@ -44,12 +44,12 @@ class Dashboard extends Component {
dashboard.processingStatsHistory ||
dashboard.processingTopStats;
const refreshFullButton = <button type="button" className="btn btn-outline-primary btn-sm" onClick={() => this.getAllStats()}><Trans>Refresh statistics</Trans></button>;
const refreshFullButton = <button type="button" className="btn btn-outline-primary btn-sm" onClick={() => this.getAllStats()}><Trans>refresh_statics</Trans></button>;
const refreshButton = <button type="button" className="btn btn-outline-primary btn-sm card-refresh" onClick={() => this.getAllStats()} />;
return (
<Fragment>
<PageTitle title={ t('Dashboard') }>
<PageTitle title={ t('dashboard') }>
<div className="page-title__actions">
{this.getToggleFilteringButton()}
{refreshFullButton}

View File

@ -18,8 +18,8 @@ class UserRules extends Component {
const { t } = this.props;
return (
<Card
title={ t('Custom filtering rules') }
subtitle={ t('Enter one rule on a line. You can use either adblock rules or hosts files syntax.') }
title={ t('custom_filter_rules') }
subtitle={ t('custom_filter_rules_hint') }
>
<form onSubmit={this.handleSubmit}>
<textarea className="form-control form-control--textarea-large" value={this.props.userRules} onChange={this.handleChange} />
@ -29,28 +29,28 @@ class UserRules extends Component {
type="submit"
onClick={this.handleSubmit}
>
<Trans>Apply</Trans>
<Trans>apply_btn</Trans>
</button>
</div>
</form>
<hr/>
<div className="list leading-loose">
<Trans>Examples</Trans>:
<Trans>examples_title</Trans>:
<ol className="leading-loose">
<li>
<code>||example.org^</code> - { t('block access to the example.org domain and all its subdomains') }
<code>||example.org^</code> - { t('example_meaning_filter_block') }
</li>
<li>
<code> @@||example.org^</code> - { t('unblock access to the example.org domain and all its subdomains') }
<code> @@||example.org^</code> - { t('example_meaning_filter_whitelist') }
</li>
<li>
<code>127.0.0.1 example.org</code> - { t('AdGuard Home will now return 127.0.0.1 address for the example.org domain (but not its subdomains).') }
<code>127.0.0.1 example.org</code> - { t('example_comment') }
</li>
<li>
<code>{ t('! Here goes a comment') }</code> - { t('just a comment') }
<code>{ t('example_comment') }</code> - { t('example_comment_meaning') }
</li>
<li>
<code>{ t('# Also a comment') }</code> - { t('just a comment') }
<code>{ t('example_comment_hash') }</code> - { t('example_comment_meaning') }
</li>
</ol>
</div>

View File

@ -34,32 +34,32 @@ class Filters extends Component {
};
columns = [{
Header: this.props.t('Enabled'),
Header: this.props.t('enabled_table_header'),
accessor: 'enabled',
Cell: this.renderCheckbox,
width: 90,
className: 'text-center',
}, {
Header: this.props.t('Name'),
Header: this.props.t('name_table_header'),
accessor: 'name',
Cell: ({ value }) => (<div className="logs__row logs__row--overflow"><span className="logs__text" title={value}>{value}</span></div>),
}, {
Header: this.props.t('Filter URL'),
Header: this.props.t('filter_url_table_header'),
accessor: 'url',
Cell: ({ value }) => (<div className="logs__row logs__row--overflow"><a href={value} target='_blank' rel='noopener noreferrer' className="link logs__text">{value}</a></div>),
}, {
Header: this.props.t('Rules count'),
Header: this.props.t('rules_count_table_header'),
accessor: 'rulesCount',
className: 'text-center',
Cell: props => props.value.toLocaleString(),
}, {
Header: this.props.t('Last time updated'),
Header: this.props.t('last_time_updated_table_header'),
accessor: 'lastUpdated',
className: 'text-center',
}, {
Header: this.props.t('Actions'),
Header: this.props.t('actions_table_header'),
accessor: 'url',
Cell: ({ value }) => (<span title={ this.props.t('Delete') } className='remove-icon fe fe-trash-2' onClick={() => this.props.removeFilter(value)}/>),
Cell: ({ value }) => (<span title={ this.props.t('delete_table_action') } className='remove-icon fe fe-trash-2' onClick={() => this.props.removeFilter(value)}/>),
className: 'text-center',
width: 75,
sortable: false,
@ -71,24 +71,24 @@ class Filters extends Component {
const { filters, userRules } = this.props.filtering;
return (
<div>
<PageTitle title={ t('Filters') } />
<PageTitle title={ t('filters') } />
<div className="content">
<div className="row">
<div className="col-md-12">
<Card
title={ t('Filters and hosts blocklists') }
subtitle={ t('AdGuard Home understands basic adblock rules and hosts files syntax.') }
title={ t('filters_and_hosts') }
subtitle={ t('filters_and_hosts_hint') }
>
<ReactTable
data={filters}
columns={this.columns}
showPagination={false}
noDataText={ t('No filters added') }
noDataText={ t('no_filters_added') }
minRows={4} // TODO find out what to show if rules.length is 0
/>
<div className="card-actions">
<button className="btn btn-success btn-standart mr-2" type="submit" onClick={this.props.toggleFilteringModal}><Trans>Add filter</Trans></button>
<button className="btn btn-primary btn-standart" type="submit" onClick={this.props.refreshFilters}><Trans>Check updates</Trans></button>
<button className="btn btn-success btn-standart mr-2" type="submit" onClick={this.props.toggleFilteringModal}><Trans>add_filter_btn</Trans></button>
<button className="btn btn-primary btn-standart" type="submit" onClick={this.props.refreshFilters}><Trans>cancel_btn</Trans></button>
</div>
</Card>
</div>
@ -106,8 +106,8 @@ class Filters extends Component {
toggleModal={this.props.toggleFilteringModal}
addFilter={this.props.addFilter}
isFilterAdded={this.props.filtering.isFilterAdded}
title={ t('New filter subscription') }
inputDescription={ t('Enter a valid URL to a filter subscription or a hosts file.') }
title={ t('new_filter_btn') }
inputDescription={ t('enter_valid_filter_url') }
/>
</div>
);

View File

@ -28,37 +28,37 @@ class Menu extends Component {
<li className="nav-item border-bottom d-lg-none" onClick={this.toggleMenu}>
<div className="nav-link nav-link--back">
<svg className="nav-icon" fill="none" height="24" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m19 12h-14"/><path d="m12 19-7-7 7-7"/></svg>
<Trans>Back</Trans>
<Trans>back</Trans>
</div>
</li>
<li className="nav-item">
<NavLink to="/" exact={true} className="nav-link">
<svg className="nav-icon" fill="none" height="24" stroke="#9aa0ac" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m3 9 9-7 9 7v11a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2-2z"/><path d="m9 22v-10h6v10"/></svg>
<Trans>Dashboard</Trans>
<Trans>dashboard</Trans>
</NavLink>
</li>
<li className="nav-item">
<NavLink to="/settings" className="nav-link">
<svg className="nav-icon" fill="none" height="24" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="3"/><path d="m19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1 -2.83 0l-.06-.06a1.65 1.65 0 0 0 -1.82-.33 1.65 1.65 0 0 0 -1 1.51v.17a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2v-.09a1.65 1.65 0 0 0 -1.08-1.51 1.65 1.65 0 0 0 -1.82.33l-.06.06a2 2 0 0 1 -2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0 -1.51-1h-.17a2 2 0 0 1 -2-2 2 2 0 0 1 2-2h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0 -.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33h.08a1.65 1.65 0 0 0 1-1.51v-.17a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 -.33 1.82v.08a1.65 1.65 0 0 0 1.51 1h.17a2 2 0 0 1 2 2 2 2 0 0 1 -2 2h-.09a1.65 1.65 0 0 0 -1.51 1z"/></svg>
<Trans>Settings</Trans>
<Trans>settings</Trans>
</NavLink>
</li>
<li className="nav-item">
<NavLink to="/filters" className="nav-link">
<svg className="nav-icon" fill="none" height="24" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m22 3h-20l8 9.46v6.54l4 2v-8.54z"/></svg>
<Trans>Filters</Trans>
<Trans>filters</Trans>
</NavLink>
</li>
<li className="nav-item">
<NavLink to="/logs" className="nav-link">
<svg className="nav-icon" fill="none" height="24" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m14 2h-8a2 2 0 0 0 -2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-12z"/><path d="m14 2v6h6"/><path d="m16 13h-8"/><path d="m16 17h-8"/><path d="m10 9h-1-1"/></svg>
<Trans>Query Log</Trans>
<Trans>query_log</Trans>
</NavLink>
</li>
<li className="nav-item">
<a href={`${REPOSITORY.URL}/wiki`} className="nav-link" target="_blank" rel="noopener noreferrer">
<svg className="nav-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#66b574" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12" y2="17"></line></svg>
<Trans>FAQ</Trans>
<Trans>faq</Trans>
</a>
</li>
</ul>

View File

@ -45,7 +45,7 @@ class Header extends Component {
</Link>
{!dashboard.proccessing && dashboard.isCoreRunning &&
<span className={badgeClass}>
<Trans>{dashboard.protectionEnabled ? 'ON' : 'OFF'}</Trans>
<Trans>{dashboard.protectionEnabled ? 'on' : 'off'}</Trans>
</span>
}
</div>

View File

@ -57,10 +57,10 @@ class Logs extends Component {
if (userRules.match(preparedBlockingRule)) {
this.props.setRules(userRules.replace(`${blockingRule}`, ''));
this.props.addSuccessToast(`${t('Rule removed from the custom filtering rules')}: ${blockingRule}`);
this.props.addSuccessToast(`${t('rule_removed_from_custom_filtering_toast')}: ${blockingRule}`);
} else if (!userRules.match(preparedUnblockingRule)) {
this.props.setRules(`${userRules}${lineEnding}${unblockingRule}\n`);
this.props.addSuccessToast(`${t('Rule added to the custom filtering rules')}: ${unblockingRule}`);
this.props.addSuccessToast(`${t('rule_added_to_custom_filtering_toast')}: ${unblockingRule}`);
}
this.props.getFilteringStatus();
@ -68,7 +68,7 @@ class Logs extends Component {
renderBlockingButton(isFiltered, domain) {
const buttonClass = isFiltered ? 'btn-outline-secondary' : 'btn-outline-danger';
const buttonText = isFiltered ? 'Unblock' : 'Block';
const buttonText = isFiltered ? 'ublock_btn' : 'block_btn';
return (
<div className="logs__action">
@ -86,13 +86,13 @@ class Logs extends Component {
renderLogs(logs) {
const { t } = this.props;
const columns = [{
Header: t('Time'),
Header: t('time_table_header'),
accessor: 'time',
maxWidth: 110,
filterable: false,
Cell: ({ value }) => (<div className="logs__row"><span className="logs__text" title={value}>{formatTime(value)}</span></div>),
}, {
Header: t('Domain name'),
Header: t('domain_name_table_header'),
accessor: 'domain',
Cell: (row) => {
const response = row.value;
@ -108,11 +108,11 @@ class Logs extends Component {
);
},
}, {
Header: t('Type'),
Header: t('type_table_header'),
accessor: 'type',
maxWidth: 60,
}, {
Header: t('Response'),
Header: t('response_table_header'),
accessor: 'response',
Cell: (row) => {
const responses = row.value;
@ -126,7 +126,7 @@ class Logs extends Component {
if (reason === 'FilteredBlackList' || reason === 'NotFilteredWhiteList') {
if (filterId === 0) {
filterName = 'Custom filtering rules';
filterName = 'custom_filter_rules';
} else {
const filterItem = Object.keys(filters)
.filter(key => filters[key].id === filterId);
@ -159,7 +159,7 @@ class Logs extends Component {
}
return (
<div className="logs__row">
<span><Trans>Empty</Trans></span>
<span><Trans>empty_response_status</Trans></span>
{this.renderTooltip(isFiltered, rule, filterName)}
</div>
);
@ -177,8 +177,8 @@ class Logs extends Component {
className="form-control"
value={filter ? filter.value : 'all'}
>
<option value="all">{ t('Show all') }</option>
<option value="filtered">{ t('Show filtered') }</option>
<option value="all">{ t('show_all_filter_type') }</option>
<option value="filtered">{ t('show_filtered_type') }</option>
</select>,
}, {
Header: t('Client'),
@ -210,13 +210,13 @@ class Logs extends Component {
defaultPageSize={50}
minRows={7}
// Text
previousText={ t('Previous') }
nextText={ t('Next') }
loadingText={ t('Loading...') }
pageText={ t('Page') }
ofText={ t('of') }
rowsText={ t('rows') }
noDataText={ t('No logs found') }
previousText={ t('previous_btn') }
nextText={ t('next_btn') }
loadingText={ t('loading_table_status') }
pageText={ t('page_table_footer_text') }
ofText={ t('of_table_footer_text') }
rowsText={ t('rows_table_footer_text') }
noDataText={ t('no_logs_found') }
defaultFilterMethod={(filter, row) => {
const id = filter.pivotId || filter.id;
return row[id] !== undefined ?
@ -269,17 +269,17 @@ class Logs extends Component {
className="btn btn-gray btn-sm mr-2"
type="submit"
onClick={() => this.props.toggleLogStatus(queryLogEnabled)}
><Trans>Disable log</Trans></button>
><Trans>disabled_log_btn</Trans></button>
<button
className="btn btn-primary btn-sm mr-2"
type="submit"
onClick={this.handleDownloadButton}
><Trans>Download log file</Trans></button>
><Trans>download_log_file_btn</Trans></button>
<button
className="btn btn-outline-primary btn-sm"
type="submit"
onClick={this.getLogs}
><Trans>Refresh</Trans></button>
><Trans>refresh_btn</Trans></button>
</Fragment>
);
}
@ -289,7 +289,7 @@ class Logs extends Component {
className="btn btn-success btn-sm mr-2"
type="submit"
onClick={() => this.props.toggleLogStatus(queryLogEnabled)}
><Trans>Enable log</Trans></button>
><Trans>enabled_log_btn</Trans></button>
);
}
@ -298,7 +298,7 @@ class Logs extends Component {
const { queryLogEnabled } = dashboard;
return (
<Fragment>
<PageTitle title={ t('Query Log') } subtitle={ t('Last 5000 DNS queries') }>
<PageTitle title={ t('query_log') } subtitle={ t('last_5000_dns_queries') }>
<div className="page-title__actions">
{this.renderButtons(queryLogEnabled)}
</div>

View File

@ -28,8 +28,8 @@ class Upstream extends Component {
return (
<Card
title={ t('Upstream DNS servers') }
subtitle={ t('If you keep this field empty, AdGuard Home will use <a href="https://1.1.1.1/" target="_blank">Cloudflare DNS</a> as an upstream. Use tls:// prefix for DNS over TLS servers.') }
title={ t('upstream_dns') }
subtitle={ t('upstream_dns_hint') }
bodyType="card-body box-body--settings"
>
<div className="row">
@ -46,14 +46,14 @@ class Upstream extends Component {
type="button"
onClick={this.handleTest}
>
<Trans>Test upstreams</Trans>
<Trans>test_upstream_btn</Trans>
</button>
<button
className="btn btn-success btn-standart"
type="submit"
onClick={this.handleSubmit}
>
<Trans>Apply</Trans>
<Trans>apply_btn</Trans>
</button>
</div>
</form>

View File

@ -12,23 +12,23 @@ class Settings extends Component {
settings = {
filtering: {
enabled: false,
title: 'Block domains using filters and hosts files',
subtitle: 'You can setup blocking rules in the <a href="#filters">Filters</a> settings.',
title: 'block_domain_use_filters_and_hosts',
subtitle: 'filters_block_toggle_hint',
},
safebrowsing: {
enabled: false,
title: 'Use AdGuard browsing security web service',
subtitle: 'AdGuard Home will check if domain is blacklisted by the browsing security web service. It will use privacy-friendly lookup API to perform the check: only a short prefix of the domain name SHA256 hash is sent to the server.',
title: 'use_adguard_browsing_sec',
subtitle: 'use_adguard_browsing_sec_hint',
},
parental: {
enabled: false,
title: 'Use AdGuard parental control web service',
subtitle: 'AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.',
title: 'use_adguard_parental',
subtitle: 'use_adguard_parental_hint',
},
safesearch: {
enabled: false,
title: 'Enforce safe search',
subtitle: 'AdGuard Home can enforce safe search in the following search engines: Google, Bing, Yandex.',
title: 'enforce_safe_search',
subtitle: 'enforce_save_search_hint',
},
};
@ -48,7 +48,7 @@ class Settings extends Component {
if (this.props.dashboard.upstreamDns.length > 0) {
this.props.testUpstream(this.props.dashboard.upstreamDns);
} else {
this.props.addErrorToast({ error: this.props.t('No servers specified') });
this.props.addErrorToast({ error: this.props.t('no_servers_specified') });
}
};
@ -65,7 +65,7 @@ class Settings extends Component {
});
}
return (
<div><Trans>No settings</Trans></div>
<div><Trans>no_settings</Trans></div>
);
}
@ -74,13 +74,13 @@ class Settings extends Component {
const { upstreamDns } = this.props.dashboard;
return (
<Fragment>
<PageTitle title={ t('Settings') } />
<PageTitle title={ t('settings') } />
{settings.processing && <Loading />}
{!settings.processing &&
<div className="content">
<div className="row">
<div className="col-md-12">
<Card title={ t('General settings') } bodyType="card-body box-body--settings">
<Card title={ t('general_settings') } bodyType="card-body box-body--settings">
<div className="form">
{this.renderSettings(settings.settingsList)}
</div>

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Trans, withNamespaces } from 'react-i18next';
import { REPOSITORY } from '../../helpers/constants';
import i18n from '../../i18n';
import i18n, { languages } from '../../i18n';
class Footer extends Component {
getYear = () => {
@ -9,8 +9,8 @@ class Footer extends Component {
return today.getFullYear();
};
changeLanguage = () => {
i18n.changeLanguage(i18n.language === 'en' ? 'vi' : 'en');
changeLanguage = (event) => {
i18n.changeLanguage(event.target.value);
}
render() {
@ -23,22 +23,24 @@ class Footer extends Component {
<div className="col-auto">
Copyright © {this.getYear()} <a href="https://adguard.com/">AdGuard</a>
</div>
<div className="col-auto">
<select className="form-control" value={i18n.language} onChange={this.changeLanguage}>
{ languages.map(language => <option
key={language.key} value={language.key}>
{language.name}
</option>) }
</select>
</div>
<div className="col-auto">
<ul className="list-inline text-center mb-0">
<li className="list-inline-item">
<a href={REPOSITORY.URL} target="_blank" rel="noopener noreferrer"><Trans>Homepage</Trans></a>
<a href={REPOSITORY.URL} target="_blank" rel="noopener noreferrer"><Trans>homepage</Trans></a>
</li>
</ul>
</div>
<div className="col-auto">
<button className="btn btn-outline-info btn-sm" onClick={this.changeLanguage}
title={i18n.language === 'en' ? 'Chuyển sang Tiếng Việt' : 'Change to English'}>
{i18n.language === 'en' ? 'English' : 'Tiếng Việt'}
</button>
</div>
<div className="col-auto">
<a href={`${REPOSITORY.URL}/issues/new`} className="btn btn-outline-primary btn-sm" target="_blank" rel="noopener noreferrer">
<Trans>Report an issue</Trans>
<Trans>report_an_issue</Trans>
</a>
</div>
</div>

View File

@ -71,8 +71,8 @@ class Modal extends Component {
if (!this.props.isFilterAdded) {
return (
<React.Fragment>
<input type="text" className={inputNameClass} placeholder={ this.props.t('Enter name') } onChange={this.handleNameChange} />
<input type="text" className={inputUrlClass} placeholder={ this.props.t('Enter URL') } onChange={this.handleUrlChange} />
<input type="text" className={inputNameClass} placeholder={ this.props.t('enter_name_hint') } onChange={this.handleNameChange} />
<input type="text" className={inputUrlClass} placeholder={ this.props.t('enter_url_hint') } onChange={this.handleUrlChange} />
{inputDescription &&
<div className="description">
{inputDescription}
@ -111,8 +111,8 @@ class Modal extends Component {
{
!this.props.isFilterAdded &&
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={this.closeModal}><Trans>Cancel</Trans></button>
<button type="button" className="btn btn-success" onClick={this.handleNext} disabled={isValidForSubmit}><Trans>Add filter</Trans></button>
<button type="button" className="btn btn-secondary" onClick={this.closeModal}><Trans>cancel_btn</Trans></button>
<button type="button" className="btn btn-success" onClick={this.handleNext} disabled={isValidForSubmit}><Trans>add_filter_btn</Trans></button>
</div>
}
</div>

View File

@ -14,13 +14,13 @@ class Popover extends Component {
const source = (
<div className="popover__list-item">
<Trans>Source</Trans>: <a className="popover__link" target="_blank" rel="noopener noreferrer" href={sourceData.url}><strong>{sourceData.name}</strong></a>
<Trans>source_label</Trans>: <a className="popover__link" target="_blank" rel="noopener noreferrer" href={sourceData.url}><strong>{sourceData.name}</strong></a>
</div>
);
const tracker = (
<div className="popover__list-item">
<Trans>Name</Trans>: <a className="popover__link" target="_blank" rel="noopener noreferrer" href={data.url}><strong>{data.name}</strong></a>
<Trans>name_table_header</Trans>: <a className="popover__link" target="_blank" rel="noopener noreferrer" href={data.url}><strong>{data.name}</strong></a>
</div>
);
@ -34,11 +34,12 @@ class Popover extends Component {
<div className="popover__body">
<div className="popover__list">
<div className="popover__list-title">
<Trans>Found in the known domains database.</Trans>
<Trans>found_in_known_domain_db</Trans>
</div>
{tracker}
<div className="popover__list-item">
<Trans>Category</Trans>: <strong><Trans>{categoryName}</Trans></strong>
<Trans>category_label</Trans>: <strong>
<Trans>{categoryName}</Trans></strong>
</div>
{source}
</div>

View File

@ -14,10 +14,10 @@ class PopoverFilter extends Component {
<div className="popover__body popover__body--filter">
<div className="popover__list">
<div className="popover__list-item popover__list-item--nowrap">
<Trans>Rule</Trans>: <strong>{this.props.rule}</strong>
<Trans>rule_label</Trans>: <strong>{this.props.rule}</strong>
</div>
{this.props.filter && <div className="popover__list-item popover__list-item--nowrap">
<Trans>Filter</Trans>: <strong>{this.props.filter}</strong>
<Trans>filter_label</Trans>: <strong>{this.props.filter}</strong>
</div>}
</div>
</div>

View File

@ -1,12 +1,12 @@
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/\w[\w_\-.]*\.[a-z]{2,8}[^\s]*$/;
export const STATS_NAMES = {
avg_processing_time: 'Average processing time',
avg_processing_time: 'average_processing_time',
blocked_filtering: 'Blocked by filters',
dns_queries: 'DNS queries',
replaced_parental: 'Blocked adult websites',
replaced_safebrowsing: 'Blocked malware/phishing',
replaced_safesearch: 'Enforced safe search',
replaced_parental: 'stats_adult',
replaced_safebrowsing: 'stats_malware_phishing',
replaced_safesearch: 'enforced_save_search',
};
export const STATUS_COLORS = {

View File

@ -3,7 +3,20 @@ import i18n from 'i18next';
import { reactI18nextModule } from 'react-i18next';
import { initReactI18n } from 'react-i18next/hooks';
import langDetect from 'i18next-browser-languagedetector';
import viResource from './__locales/vi';
import vi from './__locales/vi';
import en from './__locales/en';
export const languages = [
{
key: 'vi',
name: 'Tiếng Việt',
},
{
key: 'en',
name: 'English',
},
];
i18n
.use(langDetect)
@ -11,7 +24,8 @@ i18n
.use(reactI18nextModule) // passes i18n down to react-i18next
.init({
resources: {
vi: viResource,
vi,
en,
},
fallbackLng: 'en',
keySeparator: false, // we use content as keys