Complete translate client to Vietnamese

This commit is contained in:
Hoàng Rio 2018-10-26 09:44:23 +07:00
parent bfb7a252ad
commit 4590564fea
6 changed files with 132 additions and 54 deletions

View file

@ -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',
},
};

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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>

View file

@ -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);