mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-22 13:05:36 +03:00
Complete translate client to Vietnamese
This commit is contained in:
parent
bfb7a252ad
commit
4590564fea
6 changed files with 132 additions and 54 deletions
|
@ -38,7 +38,7 @@ export default {
|
|||
'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',
|
||||
// Setting
|
||||
// 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',
|
||||
|
@ -54,5 +54,63 @@ export default {
|
|||
'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',
|
||||
// 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',
|
||||
// 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',
|
||||
// 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',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import Card from '../ui/Card';
|
||||
|
||||
export default class UserRules extends Component {
|
||||
class UserRules extends Component {
|
||||
handleChange = (e) => {
|
||||
const { value } = e.currentTarget;
|
||||
this.props.handleRulesChange(value);
|
||||
|
@ -14,10 +15,11 @@ export default class UserRules extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<Card
|
||||
title="Custom filtering rules"
|
||||
subtitle="Enter one rule on a line. You can use either adblock rules or hosts files syntax."
|
||||
title={ t('Custom filtering rules') }
|
||||
subtitle={ t('Enter one rule on a line. You can use either adblock rules or hosts files syntax.') }
|
||||
>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<textarea className="form-control form-control--textarea-large" value={this.props.userRules} onChange={this.handleChange} />
|
||||
|
@ -27,31 +29,28 @@ export default class UserRules extends Component {
|
|||
type="submit"
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
Apply
|
||||
<Trans>Apply</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<hr/>
|
||||
<div className="list leading-loose">
|
||||
Examples:
|
||||
<Trans>Examples</Trans>:
|
||||
<ol className="leading-loose">
|
||||
<li>
|
||||
<code>||example.org^</code> - block access to the example.org domain
|
||||
and all its subdomains
|
||||
<code>||example.org^</code> - { t('block access to the example.org domain and all its subdomains') }
|
||||
</li>
|
||||
<li>
|
||||
<code> @@||example.org^</code> - unblock access to the example.org
|
||||
domain and all its subdomains
|
||||
<code> @@||example.org^</code> - { t('unblock access to the example.org domain and all its subdomains') }
|
||||
</li>
|
||||
<li>
|
||||
<code>127.0.0.1 example.org</code> - 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('AdGuard Home will now return 127.0.0.1 address for the example.org domain (but not its subdomains).') }
|
||||
</li>
|
||||
<li>
|
||||
<code>! Here goes a comment</code> - just a comment
|
||||
<code>{ t('! Here goes a comment') }</code> - { t('just a comment') }
|
||||
</li>
|
||||
<li>
|
||||
<code># Also a comment</code> - just a comment
|
||||
<code>{ t('# Also a comment') }</code> - { t('just a comment') }
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
@ -64,4 +63,7 @@ UserRules.propTypes = {
|
|||
userRules: PropTypes.string,
|
||||
handleRulesChange: PropTypes.func,
|
||||
handleRulesSubmit: PropTypes.func,
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withNamespaces()(UserRules);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import Modal from '../ui/Modal';
|
||||
import PageTitle from '../ui/PageTitle';
|
||||
import Card from '../ui/Card';
|
||||
|
@ -33,32 +34,32 @@ class Filters extends Component {
|
|||
};
|
||||
|
||||
columns = [{
|
||||
Header: 'Enabled',
|
||||
Header: this.props.t('Enabled'),
|
||||
accessor: 'enabled',
|
||||
Cell: this.renderCheckbox,
|
||||
width: 90,
|
||||
className: 'text-center',
|
||||
}, {
|
||||
Header: 'Name',
|
||||
Header: this.props.t('Name'),
|
||||
accessor: 'name',
|
||||
Cell: ({ value }) => (<div className="logs__row logs__row--overflow"><span className="logs__text" title={value}>{value}</span></div>),
|
||||
}, {
|
||||
Header: 'Filter URL',
|
||||
Header: this.props.t('Filter URL'),
|
||||
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: 'Rules count',
|
||||
Header: this.props.t('Rules count'),
|
||||
accessor: 'rulesCount',
|
||||
className: 'text-center',
|
||||
Cell: props => props.value.toLocaleString(),
|
||||
}, {
|
||||
Header: 'Last time updated',
|
||||
Header: this.props.t('Last time updated'),
|
||||
accessor: 'lastUpdated',
|
||||
className: 'text-center',
|
||||
}, {
|
||||
Header: 'Actions',
|
||||
Header: this.props.t('Actions'),
|
||||
accessor: 'url',
|
||||
Cell: ({ value }) => (<span className='remove-icon fe fe-trash-2' onClick={() => this.props.removeFilter(value)}/>),
|
||||
Cell: ({ value }) => (<span title={ this.props.t('Delete') } className='remove-icon fe fe-trash-2' onClick={() => this.props.removeFilter(value)}/>),
|
||||
className: 'text-center',
|
||||
width: 75,
|
||||
sortable: false,
|
||||
|
@ -66,27 +67,28 @@ class Filters extends Component {
|
|||
];
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
const { filters, userRules } = this.props.filtering;
|
||||
return (
|
||||
<div>
|
||||
<PageTitle title="Filters" />
|
||||
<PageTitle title={ t('Filters') } />
|
||||
<div className="content">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<Card
|
||||
title="Filters and hosts blocklists"
|
||||
subtitle="AdGuard Home understands basic adblock rules and hosts files syntax."
|
||||
title={ t('Filters and hosts blocklists') }
|
||||
subtitle={ t('AdGuard Home understands basic adblock rules and hosts files syntax.') }
|
||||
>
|
||||
<ReactTable
|
||||
data={filters}
|
||||
columns={this.columns}
|
||||
showPagination={false}
|
||||
noDataText="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}>Add filter</button>
|
||||
<button className="btn btn-primary btn-standart" type="submit" onClick={this.props.refreshFilters}>Check updates</button>
|
||||
<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>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -104,8 +106,8 @@ class Filters extends Component {
|
|||
toggleModal={this.props.toggleFilteringModal}
|
||||
addFilter={this.props.addFilter}
|
||||
isFilterAdded={this.props.filtering.isFilterAdded}
|
||||
title="New filter subscription"
|
||||
inputDescription="Enter a valid URL to a filter subscription or a hosts file."
|
||||
title={ t('New filter subscription') }
|
||||
inputDescription={ t('Enter a valid URL to a filter subscription or a hosts file.') }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -127,7 +129,8 @@ Filters.propTypes = {
|
|||
toggleFilteringModal: PropTypes.func.isRequired,
|
||||
handleRulesChange: PropTypes.func.isRequired,
|
||||
refreshFilters: PropTypes.func.isRequired,
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
|
||||
export default Filters;
|
||||
export default withNamespaces()(Filters);
|
||||
|
|
|
@ -4,6 +4,7 @@ import ReactTable from 'react-table';
|
|||
import { saveAs } from 'file-saver/FileSaver';
|
||||
import escapeRegExp from 'lodash/escapeRegExp';
|
||||
import endsWith from 'lodash/endsWith';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
|
||||
import { formatTime } from '../../helpers/helpers';
|
||||
import { getTrackerData } from '../../helpers/trackers/trackers';
|
||||
|
@ -75,21 +76,22 @@ class Logs extends Component {
|
|||
className={`btn btn-sm ${buttonClass}`}
|
||||
onClick={() => this.toggleBlocking(buttonText.toLowerCase(), domain)}
|
||||
>
|
||||
{buttonText}
|
||||
<Trans>{buttonText}</Trans>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderLogs(logs) {
|
||||
const { t } = this.props;
|
||||
const columns = [{
|
||||
Header: 'Time',
|
||||
Header: t('Time'),
|
||||
accessor: 'time',
|
||||
maxWidth: 110,
|
||||
filterable: false,
|
||||
Cell: ({ value }) => (<div className="logs__row"><span className="logs__text" title={value}>{formatTime(value)}</span></div>),
|
||||
}, {
|
||||
Header: 'Domain name',
|
||||
Header: t('Domain name'),
|
||||
accessor: 'domain',
|
||||
Cell: (row) => {
|
||||
const response = row.value;
|
||||
|
@ -105,11 +107,11 @@ class Logs extends Component {
|
|||
);
|
||||
},
|
||||
}, {
|
||||
Header: 'Type',
|
||||
Header: t('Type'),
|
||||
accessor: 'type',
|
||||
maxWidth: 60,
|
||||
}, {
|
||||
Header: 'Response',
|
||||
Header: t('Response'),
|
||||
accessor: 'response',
|
||||
Cell: (row) => {
|
||||
const responses = row.value;
|
||||
|
@ -142,7 +144,7 @@ class Logs extends Component {
|
|||
return (
|
||||
<div className="logs__row">
|
||||
{this.renderTooltip(isFiltered, rule)}
|
||||
<span>Empty</span>
|
||||
<span><Trans>Empty</Trans></span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -159,11 +161,11 @@ class Logs extends Component {
|
|||
className="form-control"
|
||||
value={filter ? filter.value : 'all'}
|
||||
>
|
||||
<option value="all">Show all</option>
|
||||
<option value="filtered">Show filtered</option>
|
||||
<option value="all">{ t('Show all') }</option>
|
||||
<option value="filtered">{ t('Show filtered') }</option>
|
||||
</select>,
|
||||
}, {
|
||||
Header: 'Client',
|
||||
Header: t('Client'),
|
||||
accessor: 'client',
|
||||
maxWidth: 250,
|
||||
Cell: (row) => {
|
||||
|
@ -191,7 +193,14 @@ class Logs extends Component {
|
|||
showPagination={true}
|
||||
defaultPageSize={50}
|
||||
minRows={7}
|
||||
noDataText="No logs found"
|
||||
// Text
|
||||
previousText={ t('Previous') }
|
||||
nextText={ t('Next') }
|
||||
loadingText={ t('Loading...') }
|
||||
pageText={ t('Page') }
|
||||
ofText={ t('of') }
|
||||
rowsText={ t('rows') }
|
||||
noDataText={ t('No logs found') }
|
||||
defaultFilterMethod={(filter, row) => {
|
||||
const id = filter.pivotId || filter.id;
|
||||
return row[id] !== undefined ?
|
||||
|
@ -233,17 +242,17 @@ class Logs extends Component {
|
|||
className="btn btn-gray btn-sm mr-2"
|
||||
type="submit"
|
||||
onClick={() => this.props.toggleLogStatus(queryLogEnabled)}
|
||||
>Disable log</button>
|
||||
><Trans>Disable log</Trans></button>
|
||||
<button
|
||||
className="btn btn-primary btn-sm mr-2"
|
||||
type="submit"
|
||||
onClick={this.handleDownloadButton}
|
||||
>Download log file</button>
|
||||
><Trans>Download log file</Trans></button>
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
type="submit"
|
||||
onClick={this.getLogs}
|
||||
>Refresh</button>
|
||||
><Trans>Refresh</Trans></button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -253,16 +262,16 @@ class Logs extends Component {
|
|||
className="btn btn-success btn-sm mr-2"
|
||||
type="submit"
|
||||
onClick={() => this.props.toggleLogStatus(queryLogEnabled)}
|
||||
>Enable log</button>
|
||||
><Trans>Enable log</Trans></button>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { queryLogs, dashboard } = this.props;
|
||||
const { queryLogs, dashboard, t } = this.props;
|
||||
const { queryLogEnabled } = dashboard;
|
||||
return (
|
||||
<Fragment>
|
||||
<PageTitle title="Query Log" subtitle="Last 5000 DNS queries">
|
||||
<PageTitle title={ t('Query Log') } subtitle={ t('Last 5000 DNS queries') }>
|
||||
<div className="page-title__actions">
|
||||
{this.renderButtons(queryLogEnabled)}
|
||||
</div>
|
||||
|
@ -288,6 +297,7 @@ Logs.propTypes = {
|
|||
userRules: PropTypes.string,
|
||||
setRules: PropTypes.func,
|
||||
addSuccessToast: PropTypes.func,
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Logs;
|
||||
export default withNamespaces()(Logs);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
class Toast extends Component {
|
||||
componentDidMount() {
|
||||
|
@ -18,7 +19,7 @@ class Toast extends Component {
|
|||
return (
|
||||
<div className={`toast toast--${this.props.type}`}>
|
||||
<p className="toast__content">
|
||||
{this.props.message}
|
||||
<Trans>{this.props.message}</Trans>
|
||||
</p>
|
||||
<button className="toast__dismiss" onClick={() => this.props.removeToast(this.props.id)}>
|
||||
<svg stroke="#fff" fill="none" width="20" height="20" strokeWidth="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m18 6-12 12"/><path d="m6 6 12 12"/></svg>
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import ReactModal from 'react-modal';
|
||||
import classnames from 'classnames';
|
||||
import { Trans, withNamespaces } from 'react-i18next';
|
||||
import { R_URL_REQUIRES_PROTOCOL } from '../../helpers/constants';
|
||||
import './Modal.css';
|
||||
|
||||
|
@ -13,7 +14,7 @@ const initialState = {
|
|||
isUrlValid: false,
|
||||
};
|
||||
|
||||
export default class Modal extends Component {
|
||||
class Modal extends Component {
|
||||
state = initialState;
|
||||
|
||||
// eslint-disable-next-line
|
||||
|
@ -70,8 +71,8 @@ export default class Modal extends Component {
|
|||
if (!this.props.isFilterAdded) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<input type="text" className={inputNameClass} placeholder="Enter name" onChange={this.handleNameChange} />
|
||||
<input type="text" className={inputUrlClass} placeholder="Enter URL" onChange={this.handleUrlChange} />
|
||||
<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} />
|
||||
{inputDescription &&
|
||||
<div className="description">
|
||||
{inputDescription}
|
||||
|
@ -81,7 +82,7 @@ export default class Modal extends Component {
|
|||
}
|
||||
return (
|
||||
<div className="description">
|
||||
Url added successfully
|
||||
<Trans>Url added successfully</Trans>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -110,8 +111,8 @@ export default class Modal extends Component {
|
|||
{
|
||||
!this.props.isFilterAdded &&
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={this.closeModal}>Cancel</button>
|
||||
<button type="button" className="btn btn-success" onClick={this.handleNext} disabled={isValidForSubmit}>Add filter</button>
|
||||
<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>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -127,4 +128,7 @@ Modal.propTypes = {
|
|||
inputDescription: PropTypes.string,
|
||||
addFilter: PropTypes.func.isRequired,
|
||||
isFilterAdded: PropTypes.bool,
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
export default withNamespaces()(Modal);
|
||||
|
|
Loading…
Reference in a new issue