1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

...

36 Commits

Author SHA1 Message Date
b54d2c9f5e chore(release): 0.7.29-beta.3 2024-10-08 18:38:02 +02:00
9a0ad80bb5 perf(MySQL): made some common errors related to corrupted tables non-blocking, closes #877 2024-10-08 18:34:29 +02:00
2f3f5de8d6 feat(translation): add hebrew translation, closes #878 2024-10-08 18:26:30 +02:00
b2c046fd38 Merge pull request #879 from antares-sql/all-contributors/add-LeviEyal
docs: add LeviEyal as a contributor for translation
2024-10-08 12:47:01 +02:00
allcontributors[bot]
bbc29a6335 docs: update .all-contributorsrc [skip ci] 2024-10-08 10:46:28 +00:00
allcontributors[bot]
b6c337638c docs: update README.md [skip ci] 2024-10-08 10:46:27 +00:00
2120a59d41 chore(release): 0.7.29-beta.2 2024-10-02 10:18:14 +02:00
2cda4a1fa1 fix(MySQL): missing exported values for DEFAULT_GENERATED table fields, fixes #854 2024-10-01 18:08:58 +02:00
76c8cd1beb Merge pull request #875 from mirrorb/master
fix(PostgreSQL): unable to change table comment to empty, error changing the comment for a specific table name
2024-09-30 18:12:02 +02:00
14aeebed9c feat(UI): new context menu and some minor improvements to query tabs, closes #867 2024-09-30 18:10:38 +02:00
mirrorb
1a1118452a Merge remote-tracking branch 'upstream/develop' 2024-09-30 11:22:46 +08:00
mirrorb
4b0f596405 Merge branch 'master' of https://github.com/antares-sql/antares 2024-09-30 11:05:02 +08:00
mirrorb
eb749f0f66 fix(PostgreSQL): error changing the comment for a specific table name 2024-09-30 11:02:26 +08:00
mirrorb
d78e59dd09 fix(PostgreSQL): unable to change table comment to empty 2024-09-30 10:57:19 +08:00
7969294a93 fix(MySQL): incorrect representation of the DATE if the year is prior to 1900, fixes #860 2024-09-29 13:50:22 +02:00
2ae016f0b6 chore(release): 0.7.29-beta.1 2024-09-28 15:50:38 +02:00
b4f33bc474 Merge branch 'develop' of https://github.com/antares-sql/antares into beta 2024-09-28 15:50:09 +02:00
f185463866 Merge branch 'master' of https://github.com/antares-sql/antares into develop 2024-09-28 15:46:48 +02:00
3fa0bd3cd1 Merge pull request #873 from antares-sql/all-contributors/add-mirrorb
docs: add mirrorb as a contributor for code
2024-09-28 15:46:16 +02:00
allcontributors[bot]
0d3ef39822 docs: update .all-contributorsrc [skip ci] 2024-09-28 13:46:03 +00:00
allcontributors[bot]
a02913f4e5 docs: update README.md [skip ci] 2024-09-28 13:46:02 +00:00
f9f993cbcd Merge pull request #872 from mirrorb/master
feat(PostgreSQL): table and field comments
2024-09-28 15:43:05 +02:00
mirrorb
ebd1a75445 feat(PostgreSQL): table and field comments 2024-09-27 17:43:54 +08:00
4201532081 Merge pull request #870 from antares-sql/all-contributors/add-zwei-c
docs: add zwei-c as a contributor for translation
2024-09-25 09:19:44 +02:00
allcontributors[bot]
c5cb586358 docs: update .all-contributorsrc [skip ci] 2024-09-25 07:19:18 +00:00
allcontributors[bot]
c111b2c0f5 docs: update README.md [skip ci] 2024-09-25 07:19:17 +00:00
b70ed124eb feat(translation): traditional chinese translation, closes #869 2024-09-25 09:18:37 +02:00
010147b553 chore(release): 0.7.29-beta.0 2024-09-23 09:12:15 +02:00
da8cc39157 fix: mismatch between table field columns and results with duplicate fields, fixes #848 2024-09-20 18:08:11 +02:00
37d44c95ee perf(UI): hide edit/delete functions in readonly mode 2024-09-18 16:42:21 +02:00
4df4c6197d Merge pull request #858 from Lawondyss/lawondyss
feat: update czech translation
2024-08-30 16:25:29 +02:00
Ladislav Vondráček
0506b653d7 feat: update czech translation 2024-08-30 15:22:19 +02:00
5fd9fe48a2 Merge pull request #853 from hatch01/master
build(deps): update various dependencies
2024-08-30 09:07:42 +02:00
b6a7124f33 feat: cancel button when waiting to connect database, closes #830 2024-08-28 18:05:04 +02:00
eymeric
5855ab0921 build(deps): update various dependencies 2024-08-26 22:15:47 +02:00
c2b602785a chore(release): 0.7.28 2024-08-20 17:48:59 +02:00
33 changed files with 12392 additions and 262 deletions

View File

@@ -293,6 +293,33 @@
"contributions": [
"code"
]
},
{
"login": "zwei-c",
"name": "CHANG, CHIH WEI",
"avatar_url": "https://avatars.githubusercontent.com/u/55912811?v=4",
"profile": "https://github.com/zwei-c",
"contributions": [
"translation"
]
},
{
"login": "mirrorb",
"name": "GaoChun",
"avatar_url": "https://avatars.githubusercontent.com/u/34116207?v=4",
"profile": "https://github.com/mirrorb",
"contributions": [
"code"
]
},
{
"login": "LeviEyal",
"name": "Eyal Levi",
"avatar_url": "https://avatars.githubusercontent.com/u/48846533?v=4",
"profile": "https://github.com/LeviEyal",
"contributions": [
"translation"
]
}
],
"contributorsPerLine": 7,

View File

@@ -2,6 +2,61 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.7.29-beta.3](https://github.com/antares-sql/antares/compare/v0.7.29-beta.2...v0.7.29-beta.3) (2024-10-08)
### Features
* **translation:** add hebrew translation, closes [#878](https://github.com/antares-sql/antares/issues/878) ([2f3f5de](https://github.com/antares-sql/antares/commit/2f3f5de8d6b02cfbf5217adfcb09a61e13d1e901))
### Improvements
* **MySQL:** made some common errors related to corrupted tables non-blocking, closes [#877](https://github.com/antares-sql/antares/issues/877) ([9a0ad80](https://github.com/antares-sql/antares/commit/9a0ad80bb55f84bd6c90cc1e9b63b33512d336a8))
### [0.7.29-beta.2](https://github.com/antares-sql/antares/compare/v0.7.29-beta.1...v0.7.29-beta.2) (2024-10-02)
### Features
* **UI:** new context menu and some minor improvements to query tabs, closes [#867](https://github.com/antares-sql/antares/issues/867) ([14aeebe](https://github.com/antares-sql/antares/commit/14aeebed9cd8e475548f5e0ade105f4b11954cb2))
### Bug Fixes
* **MySQL:** incorrect representation of the DATE if the year is prior to 1900, fixes [#860](https://github.com/antares-sql/antares/issues/860) ([7969294](https://github.com/antares-sql/antares/commit/7969294a93a51861c57d4396c7a0d89ecc7e8a84))
* **MySQL:** missing exported values for DEFAULT_GENERATED table fields, fixes [#854](https://github.com/antares-sql/antares/issues/854) ([2cda4a1](https://github.com/antares-sql/antares/commit/2cda4a1fa1c80f3567e160caf0b93bc19d76fbaa))
* **PostgreSQL:** error changing the comment for a specific table name ([eb749f0](https://github.com/antares-sql/antares/commit/eb749f0f66bf6547053e30b1503c8b2990ae5950))
* **PostgreSQL:** unable to change table comment to empty ([d78e59d](https://github.com/antares-sql/antares/commit/d78e59dd0910d3ea6ec5183a8748420b2db57050))
### [0.7.29-beta.1](https://github.com/antares-sql/antares/compare/v0.7.29-beta.0...v0.7.29-beta.1) (2024-09-28)
### Features
* **PostgreSQL:** table and field comments ([ebd1a75](https://github.com/antares-sql/antares/commit/ebd1a7544594eb4498560cc64de4b94146ee8439))
* **translation:** traditional chinese translation, closes [#869](https://github.com/antares-sql/antares/issues/869) ([b70ed12](https://github.com/antares-sql/antares/commit/b70ed124eb753091a6afe637d75e59ee9771c8eb))
### [0.7.29-beta.0](https://github.com/antares-sql/antares/compare/v0.7.28...v0.7.29-beta.0) (2024-09-23)
### Features
* cancel button when waiting to connect database, closes [#830](https://github.com/antares-sql/antares/issues/830) ([b6a7124](https://github.com/antares-sql/antares/commit/b6a7124f33397a2ae7da654b5867f6982ac5810e))
* update czech translation ([0506b65](https://github.com/antares-sql/antares/commit/0506b653d74d8cd5e848bc2ec4d29d4b0247c880))
### Bug Fixes
* mismatch between table field columns and results with duplicate fields, fixes [#848](https://github.com/antares-sql/antares/issues/848) ([da8cc39](https://github.com/antares-sql/antares/commit/da8cc39157a4b507d3d377ee1e888b8f8a52b7c5))
### Improvements
* **UI:** hide edit/delete functions in readonly mode ([37d44c9](https://github.com/antares-sql/antares/commit/37d44c95ee559f3ee1345e91fca5e2c1e86c5fbf))
### [0.7.28](https://github.com/antares-sql/antares/compare/v0.7.28-beta.0...v0.7.28) (2024-08-20)
### [0.7.28-beta.0](https://github.com/antares-sql/antares/compare/v0.7.27...v0.7.28-beta.0) (2024-08-06)

View File

@@ -152,6 +152,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bagusindrayana"><img src="https://avatars.githubusercontent.com/u/36830534?v=4?s=100" width="100px;" alt="Bagus Indrayana"/><br /><sub><b>Bagus Indrayana</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=bagusindrayana" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/penguinlab"><img src="https://avatars.githubusercontent.com/u/10959317?v=4?s=100" width="100px;" alt="Naoki Ishikawa"/><br /><sub><b>Naoki Ishikawa</b></sub></a><br /><a href="#translation-penguinlab" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://fazevedo.dev"><img src="https://avatars.githubusercontent.com/u/1640325?v=4?s=100" width="100px;" alt="Filipe Azevedo"/><br /><sub><b>Filipe Azevedo</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=mangas" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zwei-c"><img src="https://avatars.githubusercontent.com/u/55912811?v=4?s=100" width="100px;" alt="CHANG, CHIH WEI"/><br /><sub><b>CHANG, CHIH WEI</b></sub></a><br /><a href="#translation-zwei-c" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mirrorb"><img src="https://avatars.githubusercontent.com/u/34116207?v=4?s=100" width="100px;" alt="GaoChun"/><br /><sub><b>GaoChun</b></sub></a><br /><a href="https://github.com/antares-sql/antares/commits?author=mirrorb" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LeviEyal"><img src="https://avatars.githubusercontent.com/u/48846533?v=4?s=100" width="100px;" alt="Eyal Levi"/><br /><sub><b>Eyal Levi</b></sub></a><br /><a href="#translation-LeviEyal" title="Translation">🌍</a></td>
</tr>
</tbody>
</table>

10499
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "antares",
"productName": "Antares",
"version": "0.7.28-beta.0",
"version": "0.7.29-beta.3",
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
"license": "MIT",
"repository": "https://github.com/antares-sql/antares.git",

View File

@@ -62,6 +62,7 @@ export const customizations: Customizations = {
indexes: true,
foreigns: true,
nullable: true,
comment: true,
tableArray: true,
procedureSql: '$procedure$\r\n\r\n$procedure$',
procedureContext: true,

View File

@@ -18,7 +18,7 @@ export type Importer = MySQLImporter | PostgreSQLImporter
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export interface IpcResponse<T = any> {
status: 'success' | 'error';
status: 'success' | 'error' | 'abort';
response?: T;
}

View File

@@ -5,11 +5,21 @@ import { SslOptions } from 'mysql2';
import { ClientsFactory } from '../libs/ClientsFactory';
import { validateSender } from '../libs/misc/validateSender';
const isAborting: Record<string, boolean> = {};
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('test-connection', async (event, conn: antares.ConnectionParams) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
let isLocalAborted = false;
const abortChecker = setInterval(() => { // Intercepts abort request
if (isAborting[conn.uid]) {
isAborting[conn.uid] = false;
isLocalAborted = true;
clearInterval(abortChecker);
}
}, 50);
const params = {
host: conn.host,
port: +conn.port,
@@ -65,19 +75,27 @@ export default (connections: Record<string, antares.Client>) => {
client: conn.client,
params
});
await connection.connect();
if (conn.client === 'firebird')
connection.raw('SELECT rdb$get_context(\'SYSTEM\', \'DB_NAME\') FROM rdb$database');
else
await connection.select('1+1').run();
await connection.connect();
if (isLocalAborted) {
connection.destroy();
return;
}
await connection.ping();
connection.destroy();
clearInterval(abortChecker);
return { status: 'success' };
}
catch (err) {
return { status: 'error', response: err.toString() };
clearInterval(abortChecker);
if (!isLocalAborted)
return { status: 'error', response: err.toString() };
else
return { status: 'abort', response: 'Connection aborted' };
}
});
@@ -88,6 +106,15 @@ export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('connect', async (event, conn: antares.ConnectionParams) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
let isLocalAborted = false;
const abortChecker = setInterval(() => { // Intercepts abort request
if (isAborting[conn.uid]) {
isAborting[conn.uid] = false;
isLocalAborted = true;
clearInterval(abortChecker);
}
}, 50);
const params = {
host: conn.host,
port: +conn.port,
@@ -150,18 +177,36 @@ export default (connections: Record<string, antares.Client>) => {
});
await connection.connect();
if (isLocalAborted) {
connection.destroy();
return { status: 'abort', response: 'Connection aborted' };
}
const structure = await connection.getStructure(new Set());
if (isLocalAborted) {
connection.destroy();
return { status: 'abort', response: 'Connection aborted' };
}
connections[conn.uid] = connection;
clearInterval(abortChecker);
return { status: 'success', response: structure };
}
catch (err) {
return { status: 'error', response: err.toString() };
clearInterval(abortChecker);
if (!isLocalAborted)
return { status: 'error', response: err.toString() };
else
return { status: 'abort', response: 'Connection aborted' };
}
});
ipcMain.on('abort-connection', (event, uid) => {
isAborting[uid] = true;
});
ipcMain.handle('disconnect', (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -3,14 +3,25 @@ import mysql from 'mysql2/promise';
import * as pg from 'pg';
import SSH2Promise = require('@fabio286/ssh2-promise');
const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
// Remove comments, newlines and multiple spaces
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
export type LoggerLevel = 'query' | 'error'
const ipcLogger = ({ content, cUid, level }: {content: string; cUid: string; level: LoggerLevel}) => {
if (level === 'error') {
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('non-blocking-exception', { cUid, message: content, date: new Date() });
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(content);
}
else if (level === 'query') {
// Remove comments, newlines and multiple spaces
const escapedSql = content.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
if (process.type !== undefined) {
const mainWindow = require('electron').webContents.fromId(1);
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql);
}
if (process.env.NODE_ENV === 'development' && process.type === 'browser') console.log(escapedSql);
};
/**
@@ -22,7 +33,7 @@ export abstract class BaseClient {
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};
protected _poolSize: number;
protected _ssh?: SSH2Promise;
protected _logger: (args: {sql: string; cUid: string}) => void;
protected _logger: (args: {content: string; cUid: string; level: LoggerLevel}) => void;
protected _queryDefaults: antares.QueryBuilderObject;
protected _query: antares.QueryBuilderObject;
@@ -31,7 +42,7 @@ export abstract class BaseClient {
this._cUid = args.uid;
this._params = args.params;
this._poolSize = args.poolSize || undefined;
this._logger = args.logger || queryLogger;
this._logger = args.logger || ipcLogger;
this._queryDefaults = {
schema: '',

View File

@@ -109,6 +109,10 @@ export class FirebirdSQLClient extends BaseClient {
return firebird.pool(this._poolSize, { ...this._params, blobAsText: true });
}
ping () {
return this.raw('SELECT rdb$get_context(\'SYSTEM\', \'DB_NAME\') FROM rdb$database');
}
destroy () {
if (this._poolSize)
return (this._connection as firebird.ConnectionPool).destroy();
@@ -1020,7 +1024,7 @@ export class FirebirdSQLClient extends BaseClient {
alias: string;
}
this._logger({ cUid: this._cUid, sql });
this._logger({ cUid: this._cUid, content: sql, level: 'query' });
args = {
nest: false,

View File

@@ -214,6 +214,10 @@ export class MySQLClient extends BaseClient {
}
}
ping () {
return this.select('1+1').run();
}
destroy () {
this._connection.end();
clearInterval(this._keepaliveTimer);
@@ -228,12 +232,13 @@ export class MySQLClient extends BaseClient {
const dbConfig = await this.getDbConfig();
const connection = await mysql.createConnection({
...dbConfig,
typeCast: (field, next) => {
if (field.type === 'DATETIME')
return field.string();
else
return next();
}
dateStrings: true
// typeCast: (field, next) => {
// if (field.type === 'DATETIME')
// return field.string();
// else
// return next();
// }
});
return connection;
@@ -245,12 +250,13 @@ export class MySQLClient extends BaseClient {
...dbConfig,
connectionLimit: this._poolSize,
enableKeepAlive: true,
typeCast: (field, next) => {
if (field.type === 'DATETIME')
return field.string();
else
return next();
}
dateStrings: true
// typeCast: (field, next) => {
// if (field.type === 'DATETIME')
// return field.string();
// else
// return next();
// }
});
this._keepaliveTimer = setInterval(async () => {
@@ -348,10 +354,21 @@ export class MySQLClient extends BaseClient {
if (this._params.schema)
filteredDatabases = filteredDatabases.filter(db => db.Database === this._params.schema);
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/* eslint-disable @typescript-eslint/no-explicit-any */
let functions: any[] = [];
let procedures: any[] = [];
let schedulers: any[] = [];
/* eslint-enable @typescript-eslint/no-explicit-any */
try {
const { rows: functionRows } = await this.raw('SHOW FUNCTION STATUS');
const { rows: procedureRows } = await this.raw('SHOW PROCEDURE STATUS');
functions = functionRows;
procedures = procedureRows;
}
catch (err) {
this._logger({ content: err.sqlMessage, cUid: this._cUid, level: 'error' });
}
try { // Avoid exception with event_scheduler DISABLED with MariaDB 10
const { rows } = await this.raw('SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`');
@@ -657,7 +674,7 @@ export class MySQLClient extends BaseClient {
charset: field.CHARACTER_SET_NAME,
collation: field.COLLATION_NAME,
autoIncrement: field.EXTRA.includes('auto_increment'),
generated: field.EXTRA.toLowerCase().includes('generated'),
generated: ['VIRTUAL GENERATED', 'VIRTUAL STORED'].includes(field.EXTRA),
onUpdate: field.EXTRA.toLowerCase().includes('on update')
? field.EXTRA.substr(field.EXTRA.indexOf('on update') + 9, field.EXTRA.length).trim()
: '',
@@ -1661,7 +1678,7 @@ export class MySQLClient extends BaseClient {
}
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
this._logger({ cUid: this._cUid, sql });
this._logger({ cUid: this._cUid, content: sql, level: 'query' });
args = {
nest: false,

View File

@@ -243,6 +243,10 @@ export class PostgreSQLClient extends BaseClient {
return connection;
}
ping () {
return this.select('1+1').run();
}
destroy () {
this._connection.end();
clearInterval(this._keepaliveTimer);
@@ -494,16 +498,27 @@ export class PostgreSQLClient extends BaseClient {
column_default: string;
character_set_name: string;
collation_name: string;
column_comment: string;
}
/* eslint-enable camelcase */
const { rows } = await this
.select('*')
.schema('information_schema')
.from('columns')
.where({ table_schema: `= '${schema}'`, table_name: `= '${table}'` })
.orderBy({ ordinal_position: 'ASC' })
.run<TableColumnsResult>();
// Table columns
const { rows } = await this.raw<antares.QueryResult<TableColumnsResult>>(`
WITH comments AS (
SELECT attr.attname AS column, des.description AS comment, pgc.relname
FROM pg_attribute AS attr, pg_description AS des, pg_class AS pgc
WHERE pgc.oid = attr.attrelid
AND des.objoid = pgc.oid
AND pg_table_is_visible(pgc.oid)
AND attr.attnum = des.objsubid
)
SELECT cols.*, comments.comment AS column_comment
FROM "information_schema"."columns" AS cols
LEFT JOIN comments ON comments.column = cols.column_name AND comments.relname = cols.table_name
WHERE cols.table_schema = '${schema}'
AND cols.table_name = '${table}'
ORDER BY "ordinal_position" ASC
`);
return rows.map(field => {
let type = field.data_type;
@@ -532,7 +547,7 @@ export class PostgreSQLClient extends BaseClient {
collation: field.collation_name,
autoIncrement: false,
onUpdate: null,
comment: ''
comment: field.column_comment
};
});
}
@@ -869,6 +884,7 @@ export class PostgreSQLClient extends BaseClient {
const newIndexes: string[] = [];
const manageIndexes: string[] = [];
const newForeigns: string[] = [];
const modifyComment: string[] = [];
let sql = `CREATE TABLE "${schema}"."${options.name}"`;
@@ -884,6 +900,8 @@ export class PostgreSQLClient extends BaseClient {
${field.nullable ? 'NULL' : 'NOT NULL'}
${field.default !== null ? `DEFAULT ${field.default || '\'\''}` : ''}
${field.onUpdate ? `ON UPDATE ${field.onUpdate}` : ''}`);
if (field.comment != null)
modifyComment.push(`COMMENT ON COLUMN "${schema}"."${options.name}"."${field.name}" IS '${field.comment}'`);
});
// ADD INDEX
@@ -904,8 +922,12 @@ export class PostgreSQLClient extends BaseClient {
newForeigns.push(`CONSTRAINT "${foreign.constraintName}" FOREIGN KEY ("${foreign.field}") REFERENCES "${schema}"."${foreign.refTable}" ("${foreign.refField}") ON UPDATE ${foreign.onUpdate} ON DELETE ${foreign.onDelete}`);
});
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns].join(', ')})`;
if (manageIndexes.length) sql = `${sql}; ${manageIndexes.join(';')}`;
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns].join(', ')}); `;
if (manageIndexes.length) sql = `${sql} ${manageIndexes.join(';')}; `;
// TABLE COMMENT
if (options.comment != null) sql = `${sql} COMMENT ON TABLE "${schema}"."${options.name}" IS '${options.comment}'; `;
// FIELDS COMMENT
if (modifyComment.length) sql = `${sql} ${modifyComment.join(';')}; `;
return await this.raw(sql);
}
@@ -930,6 +952,7 @@ export class PostgreSQLClient extends BaseClient {
const renameColumns: string[] = [];
const createSequences: string[] = [];
const manageIndexes: string[] = [];
const modifyComment: string[] = [];
// ADD FIELDS
additions.forEach(addition => {
@@ -943,6 +966,8 @@ export class PostgreSQLClient extends BaseClient {
${addition.nullable ? 'NULL' : 'NOT NULL'}
${addition.default !== null ? `DEFAULT ${addition.default || '\'\''}` : ''}
${addition.onUpdate ? `ON UPDATE ${addition.onUpdate}` : ''}`);
if (addition.comment != null)
modifyComment.push(`COMMENT ON COLUMN "${schema}"."${table}"."${addition.name}" IS '${addition.comment}'`);
});
// ADD INDEX
@@ -995,6 +1020,8 @@ export class PostgreSQLClient extends BaseClient {
if (change.orgName !== change.name)
renameColumns.push(`ALTER TABLE "${schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`);
if (change.comment != null)
modifyComment.push(`COMMENT ON COLUMN "${schema}"."${table}"."${change.name}" IS '${change.comment}'`);
});
// CHANGE INDEX
@@ -1042,8 +1069,11 @@ export class PostgreSQLClient extends BaseClient {
if (alterColumns.length) sql += `ALTER TABLE "${schema}"."${table}" ${alterColumns.join(', ')}; `;
if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`;
if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`;
// TABLE COMMENT
if (options.comment != null) sql = `${sql} COMMENT ON TABLE "${schema}"."${table}" IS '${options.comment}'; `;
// FIELDS COMMENT
if (modifyComment.length) sql = `${sql} ${modifyComment.join(';')}; `;
if (options.name) sql += `ALTER TABLE "${schema}"."${table}" RENAME TO "${options.name}"; `;
// RENAME
if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`;
@@ -1615,7 +1645,7 @@ export class PostgreSQLClient extends BaseClient {
}
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
this._logger({ cUid: this._cUid, sql });
this._logger({ cUid: this._cUid, content: sql, level: 'query' });
args = {
nest: false,

View File

@@ -35,6 +35,10 @@ export class SQLiteClient extends BaseClient {
});
}
ping () {
return this.select('1+1').run();
}
destroy () {
this._connection.close();
}
@@ -608,7 +612,7 @@ export class SQLiteClient extends BaseClient {
}
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
this._logger({ cUid: this._cUid, sql });// TODO: replace BLOB content with a placeholder
this._logger({ cUid: this._cUid, content: sql, level: 'query' });// TODO: replace BLOB content with a placeholder
args = {
nest: false,

View File

@@ -141,8 +141,11 @@ onMounted(() => {
while (node) {
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
InputMenu.popup({ window: getCurrentWindow() });
break;
if (!node.parentNode.className.split(' ').includes('editor-query')) {
InputMenu.popup({ window: getCurrentWindow() });
console.log(node.parentNode.className);
break;
}
}
node = node.parentNode;
}

View File

@@ -612,7 +612,7 @@ const otherContributors = computed(() => {
return contributors
.split(',')
.filter(c => !c.includes(appAuthor))
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
.sort((a, b) => a.toLowerCase().trim().localeCompare(b.toLowerCase()));
});
const selectTab = (tab: string) => {

View File

@@ -3,6 +3,7 @@
<div
:id="`editor-${id}`"
class="editor"
:class="editorClasses"
:style="{height: `${height}px`}"
/>
</div>
@@ -54,7 +55,8 @@ const props = defineProps({
schema: { type: String, default: '' },
autoFocus: { type: Boolean, default: false },
readOnly: { type: Boolean, default: false },
height: { type: Number, default: 200 }
height: { type: Number, default: 200 },
editorClasses: { type: String, default: '' }
});
const emit = defineEmits(['update:modelValue']);
@@ -405,18 +407,17 @@ defineExpose({ editor });
.ace_gutter-cell.ace_breakpoint {
&::before {
content: '\F0403';
content: '';
position: absolute;
left: 3px;
top: 2px;
color: var(--primary-color);
left: 0px;
top: 8px;
display: inline-block;
font: normal normal normal 24px/1 "Material Design Icons", sans-serif;
font-size: inherit;
text-rendering: auto;
line-height: inherit;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-top: 8px solid transparent;
border-right: 8px solid var(--primary-color);
transform: rotate(-45deg);
}
}
</style>

View File

@@ -386,20 +386,35 @@
</div>
</div>
<div class="panel-footer">
<button
id="connection-test"
class="btn btn-gray mr-2 d-flex"
:class="{'loading': isTesting}"
:disabled="isBusy"
@click="startTest"
<div
@mouseenter="setCancelTestButtonVisibility(true)"
@mouseleave="setCancelTestButtonVisibility(false)"
>
<BaseIcon
icon-name="mdiLightningBolt"
:size="24"
class="mr-1"
/>
{{ t('connection.testConnection') }}
</button>
<button
v-if="showTestCancel && isTesting"
class="btn btn-gray mr-2 cancellable"
:title="t('general.cancel')"
@click="abortConnection()"
>
<BaseIcon icon-name="mdiWindowClose" :size="24" />
<span class="d-invisible pr-1">{{ t('connection.testConnection') }}</span>
</button>
<button
v-else
id="connection-test"
class="btn btn-gray mr-2 d-flex"
:class="{'loading': isTesting}"
:disabled="isBusy"
@click="startTest"
>
<BaseIcon
icon-name="mdiLightningBolt"
:size="24"
class="mr-1"
/>
{{ t('connection.testConnection') }}
</button>
</div>
<button
id="connection-save"
class="btn btn-primary mr-2 d-flex"
@@ -494,6 +509,8 @@ const firstInput: Ref<HTMLInputElement> = ref(null);
const isConnecting = ref(false);
const isTesting = ref(false);
const isAsking = ref(false);
const showTestCancel = ref(false);
const abortController: Ref<AbortController> = ref(new AbortController());
const selectedTab = ref('general');
const clientCustomizations = computed(() => {
@@ -516,6 +533,10 @@ const setDefaults = () => {
connection.value.database = clientCustomizations.value.defaultDatabase;
};
const setCancelTestButtonVisibility = (val: boolean) => {
showTestCancel.value = val;
};
const startTest = async () => {
isTesting.value = true;
@@ -526,7 +547,7 @@ const startTest = async () => {
const res = await Connection.makeTest(connection.value);
if (res.status === 'error')
addNotification({ status: 'error', message: res.response.message || res.response.toString() });
else
else if (res.status === 'success')
addNotification({ status: 'success', message: t('connection.connectionSuccessfullyMade') });
}
catch (err) {
@@ -537,13 +558,21 @@ const startTest = async () => {
}
};
const abortConnection = (): void => {
abortController.value.abort();
Connection.abortConnection(connection.value.uid);
isTesting.value = false;
isConnecting.value = false;
abortController.value = new AbortController();
};
const continueTest = async (credentials: { user: string; password: string }) => { // if "Ask for credentials" is true
isAsking.value = false;
const params = Object.assign({}, connection.value, credentials);
try {
if (isConnecting.value) {
await connectWorkspace(params);
await connectWorkspace(params, { signal: abortController.value.signal }).catch(() => undefined);
isConnecting.value = false;
}
else {

View File

@@ -387,20 +387,35 @@
</div>
</div>
<div class="panel-footer">
<button
id="connection-test"
class="btn btn-gray mr-2 d-flex"
:class="{'loading': isTesting}"
:disabled="isBusy"
@click="startTest"
<div
@mouseenter="setCancelTestButtonVisibility(true)"
@mouseleave="setCancelTestButtonVisibility(false)"
>
<BaseIcon
icon-name="mdiLightningBolt"
:size="24"
class="mr-1"
/>
{{ t('connection.testConnection') }}
</button>
<button
v-if="showTestCancel && isTesting"
class="btn btn-gray mr-2 cancellable"
:title="t('general.cancel')"
@click="abortConnection()"
>
<BaseIcon icon-name="mdiWindowClose" :size="24" />
<span class="d-invisible pr-1">{{ t('connection.testConnection') }}</span>
</button>
<button
v-else
id="connection-test"
class="btn btn-gray mr-2 d-flex"
:class="{'loading': isTesting}"
:disabled="isBusy"
@click="startTest"
>
<BaseIcon
icon-name="mdiLightningBolt"
:size="24"
class="mr-1"
/>
{{ t('connection.testConnection') }}
</button>
</div>
<button
id="connection-save"
class="btn btn-primary mr-2 d-flex"
@@ -414,20 +429,35 @@
/>
{{ t('general.save') }}
</button>
<button
id="connection-connect"
class="btn btn-success d-flex"
:class="{'loading': isConnecting}"
:disabled="isBusy"
@click="startConnection"
<div
@mouseenter="setCancelConnectButtonVisibility(true)"
@mouseleave="setCancelConnectButtonVisibility(false)"
>
<BaseIcon
icon-name="mdiConnection"
:size="24"
class="mr-1"
/>
{{ t('connection.connect') }}
</button>
<button
v-if="showConnectCancel && isConnecting"
class="btn btn-success cancellable"
:title="t('general.cancel')"
@click="abortConnection()"
>
<BaseIcon icon-name="mdiWindowClose" :size="24" />
<span class="d-invisible pr-1">{{ t('connection.connect') }}</span>
</button>
<button
v-else
id="connection-connect"
class="btn btn-success d-flex"
:class="{'loading': isConnecting}"
:disabled="isBusy"
@click="startConnection"
>
<BaseIcon
icon-name="mdiConnection"
:size="24"
class="mr-1"
/>
{{ t('connection.connect') }}
</button>
</div>
</div>
</div>
<ModalAskCredentials
@@ -476,6 +506,9 @@ const localConnection: Ref<ConnectionParams & { pgConnString: string }> = ref(nu
const isConnecting = ref(false);
const isTesting = ref(false);
const isAsking = ref(false);
const showTestCancel = ref(false);
const showConnectCancel = ref(false);
const abortController: Ref<AbortController> = ref(new AbortController());
const selectedTab = ref('general');
const clientCustomizations = computed(() => {
@@ -501,7 +534,7 @@ const startConnection = async () => {
if (localConnection.value.ask)
isAsking.value = true;
else {
await connectWorkspace(localConnection.value);
await connectWorkspace(localConnection.value, { signal: abortController.value.signal }).catch(() => undefined);
isConnecting.value = false;
}
};
@@ -516,7 +549,7 @@ const startTest = async () => {
const res = await Connection.makeTest(localConnection.value);
if (res.status === 'error')
addNotification({ status: 'error', message: res.response.message || res.response.toString() });
else
else if (res.status === 'success')
addNotification({ status: 'success', message: t('connection.connectionSuccessfullyMade') });
}
catch (err) {
@@ -527,20 +560,36 @@ const startTest = async () => {
}
};
const setCancelTestButtonVisibility = (val: boolean) => {
showTestCancel.value = val;
};
const setCancelConnectButtonVisibility = (val: boolean) => {
showConnectCancel.value = val;
};
const abortConnection = (): void => {
abortController.value.abort();
Connection.abortConnection(localConnection.value.uid);
isTesting.value = false;
isConnecting.value = false;
abortController.value = new AbortController();
};
const continueTest = async (credentials: {user: string; password: string }) => { // if "Ask for credentials" is true
isAsking.value = false;
const params = Object.assign({}, localConnection.value, credentials);
try {
if (isConnecting.value) {
const params = Object.assign({}, props.connection, credentials);
await connectWorkspace(params);
await connectWorkspace(params, { signal: abortController.value.signal }).catch(() => undefined);
isConnecting.value = false;
}
else {
const res = await Connection.makeTest(params);
if (res.status === 'error')
addNotification({ status: 'error', message: res.response.message || res.response.toString() });
else
else if (res.status === 'success')
addNotification({ status: 'success', message: t('connection.connectionSuccessfullyMade') });
}
}

View File

@@ -16,7 +16,7 @@
/> {{ t('general.run') }}</span>
</div>
<div
v-if="selectedMisc.type === 'trigger' && customizations.triggerEnableDisable"
v-if="selectedMisc.type === 'trigger' && customizations.triggerEnableDisable && !connection.readonly"
class="context-element"
@click="toggleTrigger"
>
@@ -36,7 +36,7 @@
</span>
</div>
<div
v-if="selectedMisc.type === 'scheduler'"
v-if="selectedMisc.type === 'scheduler' && !connection.readonly"
class="context-element"
@click="toggleScheduler"
>
@@ -63,7 +63,11 @@
:size="18"
/> {{ t('general.copyName') }}</span>
</div>
<div class="context-element" @click="showDeleteModal">
<div
v-if="!connection.readonly"
class="context-element"
@click="showDeleteModal"
>
<span class="d-flex">
<BaseIcon
class="text-light mt-1 mr-1"
@@ -117,6 +121,7 @@ import Routines from '@/ipc-api/Routines';
import Schedulers from '@/ipc-api/Schedulers';
import Triggers from '@/ipc-api/Triggers';
import { copyText } from '@/libs/copyText';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
@@ -132,6 +137,7 @@ const emit = defineEmits(['close-context', 'reload']);
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getConnectionByUid } = useConnectionsStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
@@ -154,6 +160,7 @@ const workspace = computed(() => {
const customizations = computed(() => {
return getWorkspace(selectedWorkspace.value).customizations;
});
const connection = computed(() => getConnectionByUid(selectedWorkspace.value));
const deleteMessage = computed(() => {
switch (props.selectedMisc.type) {

View File

@@ -3,7 +3,7 @@
:context-event="contextEvent"
@close-context="closeContext"
>
<div class="context-element">
<div v-if="!connection.readonly" class="context-element">
<span class="d-flex">
<BaseIcon
class="text-light mt-1 mr-1"
@@ -135,7 +135,7 @@
/> {{ t('database.export') }}</span>
</div>
<div
v-if="workspace.customizations.schemaImport"
v-if="workspace.customizations.schemaImport && !connection.readonly"
class="context-element"
@click="initImport"
>
@@ -147,7 +147,7 @@
/> {{ t('database.import') }}</span>
</div>
<div
v-if="workspace.customizations.schemaEdit"
v-if="workspace.customizations.schemaEdit && !connection.readonly"
class="context-element"
@click="showEditModal"
>
@@ -159,7 +159,7 @@
/> {{ t('database.editSchema') }}</span>
</div>
<div
v-if="workspace.customizations.schemaDrop"
v-if="workspace.customizations.schemaDrop && !connection.readonly"
class="context-element"
@click="showDeleteModal"
>
@@ -219,6 +219,7 @@ import ModalImportSchema from '@/components/ModalImportSchema.vue';
import Application from '@/ipc-api/Application';
import Schema from '@/ipc-api/Schema';
import { copyText } from '@/libs/copyText';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import { useSchemaExportStore } from '@/stores/schemaExport';
import { useWorkspacesStore } from '@/stores/workspaces';
@@ -248,7 +249,9 @@ const workspacesStore = useWorkspacesStore();
const schemaExportStore = useSchemaExportStore();
const { showExportModal } = schemaExportStore;
const connectionsStore = useConnectionsStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getConnectionByUid } = connectionsStore;
const {
getWorkspace,
@@ -261,6 +264,7 @@ const isEditModal = ref(false);
const isImportSchemaModal = ref(false);
const workspace = computed(() => getWorkspace(selectedWorkspace.value));
const connection = computed(() => getConnectionByUid(selectedWorkspace.value));
const openCreateTableTab = () => {
emit('open-create-table-tab');

View File

@@ -60,7 +60,7 @@
/> {{ t('application.settings') }}</span>
</div>
<div
v-if="selectedTable && selectedTable.type === 'table' && customizations.tableDuplicate"
v-if="selectedTable && selectedTable.type === 'table' && customizations.tableDuplicate && !connection.readonly"
class="context-element"
@click="duplicateTable"
>
@@ -72,7 +72,7 @@
/> {{ t('database.duplicateTable') }}</span>
</div>
<div
v-if="selectedTable && selectedTable.type === 'table'"
v-if="selectedTable && selectedTable.type === 'table' && !connection.readonly"
class="context-element"
@click="showEmptyModal"
>
@@ -83,7 +83,11 @@
:size="18"
/> {{ t('database.emptyTable') }}</span>
</div>
<div class="context-element" @click="showDeleteModal">
<div
v-if="!connection.readonly"
class="context-element"
@click="showDeleteModal"
>
<span class="d-flex">
<BaseIcon
class="text-light mt-1 mr-1"
@@ -151,6 +155,7 @@ import BaseContextMenu from '@/components/BaseContextMenu.vue';
import BaseIcon from '@/components/BaseIcon.vue';
import Tables from '@/ipc-api/Tables';
import { copyText } from '@/libs/copyText';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import { useSchemaExportStore } from '@/stores/schemaExport';
import { useWorkspacesStore } from '@/stores/workspaces';
@@ -168,6 +173,7 @@ const emit = defineEmits(['close-context', 'duplicate-table', 'reload', 'delete-
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { showExportModal } = useSchemaExportStore();
const { getConnectionByUid } = useConnectionsStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
@@ -185,6 +191,7 @@ const forceTruncate = ref(false);
const workspace = computed(() => getWorkspace(selectedWorkspace.value));
const customizations = computed(() => workspace.value && workspace.value.customizations ? workspace.value.customizations : null);
const connection = computed(() => getConnectionByUid(selectedWorkspace.value));
const showTableExportModal = () => {
showExportModal(props.selectedSchema, props.selectedTable.name);

View File

@@ -15,6 +15,7 @@
:schema="breadcrumbsSchema"
:is-selected="isSelected"
:height="editorHeight"
editor-classes="editor-query"
/>
<div ref="resizer" class="query-area-resizer" />
<div ref="queryAreaFooter" class="workspace-query-runner-footer">
@@ -273,6 +274,7 @@
</template>
<script setup lang="ts">
import { getCurrentWindow, Menu } from '@electron/remote';
import { Ace } from 'ace-builds';
import { ConnectionParams } from 'common/interfaces/antares';
import { uidGen } from 'common/libs/uidGen';
@@ -475,6 +477,8 @@ const runQuery = async (query: string) => {
saveHistory(params);
if (!autocommit.value)
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: true });
queryEditor.value.editor.focus();
}
else
addNotification({ status: 'error', message: response });
@@ -739,7 +743,11 @@ const openFile = async () => {
const saveFileAs = async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any = await Application.showSaveDialog({ filters: [{ name: 'SQL', extensions: ['sql'] }], defaultPath: `${queryName.value || 'query'}.sql` });
const result: any = await Application.showSaveDialog({
filters: [{ name: 'SQL', extensions: ['sql'] }],
defaultPath: (!queryName.value.includes('.sql') ? `${queryName.value}.sql` :queryName.value) || 'query.sql'
});
if (result && !result.canceled) {
await Application.writeFile(result.filePath, query.value);
addNotification({ status: 'success', message: t('general.actionSuccessful', { action: t('application.saveFile') }) });
@@ -750,9 +758,13 @@ const saveFileAs = async () => {
};
const saveFile = async () => {
await Application.writeFile(filePath.value, query.value);
addNotification({ status: 'success', message: t('general.actionSuccessful', { action: t('application.saveFile') }) });
lastSavedQuery.value = toRaw(query.value);
if (filePath.value) {
await Application.writeFile(filePath.value, query.value);
addNotification({ status: 'success', message: t('general.actionSuccessful', { action: t('application.saveFile') }) });
lastSavedQuery.value = toRaw(query.value);
}
else
saveFileAs();
};
const loadFileContent = async (file: string) => {
@@ -785,6 +797,67 @@ onMounted(() => {
if (props.tab.filePath)
loadFileContent(props.tab.filePath);
queryEditor.value.editor.container.addEventListener('contextmenu', (e) => {
const InputMenu = Menu.buildFromTemplate([
{
label: t('general.run'),
click: () => runQuery(query.value)
},
{
label: t('general.clear'),
click: () => clear()
},
{
type: 'separator'
},
{
label: t('application.saveFile'),
click: () => saveFile()
},
{
label: t('application.saveFileAs'),
click: () => saveFileAs()
},
{
label: t('application.openFile'),
click: () => openFile()
},
{
type: 'separator'
},
{
label: t('general.cut'),
role: 'cut'
},
{
label: t('general.copy'),
role: 'copy'
},
{
label: t('general.paste'),
role: 'paste'
},
{
type: 'separator'
},
{
label: t('general.selectAll'),
role: 'selectAll'
}
]);
e.preventDefault();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let node: any = e.target;
while (node) {
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
InputMenu.popup({ window: getCurrentWindow() });
break;
}
node = node.parentNode;
}
});
});
onBeforeUnmount(() => {

View File

@@ -38,7 +38,7 @@
<div class="thead">
<div class="tr">
<div
v-for="(field, index) in fields"
v-for="(field, index) in filteredFields"
:key="index"
class="th c-hand"
:title="`${field.type} ${fieldLength(field) ? `(${fieldLength(field)})` : ''}`"
@@ -383,6 +383,11 @@ const sortedResults = computed(() => {
const resultsWithRows = computed(() => props.results.filter(result => result.rows.length));
const fields = computed(() => resultsWithRows.value.length ? resultsWithRows.value[resultsetIndex.value].fields : []);
const filteredFields = computed(() => fields.value.reduce((acc, cur) => {
if (acc.findIndex(f => JSON.stringify(f) === JSON.stringify(cur)))
acc.push(cur);
return acc;
}, [] as TableField[]));
const keyUsage = computed(() => resultsWithRows.value.length ? resultsWithRows.value[resultsetIndex.value].keys : []);
const fieldsObj = computed(() => {

View File

@@ -1,3 +1,13 @@
/**
* [TRANSLATION UPDATE HELPER]
* - Open a terminal in antares folder and run `npm run translation:check short-code` replacing short-code with the one you are updating.
* - The command will output which terms are missing or not translated from english.
* - Open antares folder with your editor of choice.
* - Go to antares/src/renderer/i18n/ and open the locale file you want to translate.
* - Add and translate missing terms and consider whether to translate untranslated terms.
*/
export const csCZ = {
general: { // General purpose terms
edit: 'Upravit',
@@ -18,7 +28,7 @@ export const csCZ = {
download: 'Stáhnout',
add: 'Přidat',
data: 'Data',
properties: 'Vlastnosti', // Lawondyss: Not used
properties: 'Vlastnosti',
name: 'Název',
clear: 'Vyčistit',
options: 'Možnosti',
@@ -39,6 +49,7 @@ export const csCZ = {
new: 'Nové',
select: 'Vybrat',
change: 'Změnit',
include: 'Včetně',
includes: 'Zahrnout',
completed: 'Dokončeno',
aborted: 'Zrušeno',
@@ -46,15 +57,15 @@ export const csCZ = {
enable: 'Zapnuto',
disable: 'Vypnout',
contributors: 'Přispěvatelé',
pin: 'Připnout', // Lawondyss: Not used
unpin: 'Odepnout', // Lawondyss: Not used
folder: 'Složka | Složky', // Lawondyss: Used only 1
pin: 'Připnout',
unpin: 'Odepnout',
folder: 'Složka | Složky',
none: 'Nic',
singleQuote: 'Apostrofy',
doubleQuote: 'Uvozovky',
deleteConfirm: 'Skutečně chcete smazat',
uploadFile: 'Nahrát soubor',
format: 'Formátovat', // Format code
format: 'Formátovat',
history: 'Historie',
filter: 'Filtrovat',
manualValue: 'Vlastní hodnota',
@@ -64,9 +75,16 @@ export const csCZ = {
actionSuccessful: '{action} úspěšný',
outputFormat: 'Formát výstupu',
singleFile: 'Jediný {ext} soubor',
zipCompressedFile: 'ZIP komprimovaný {ext} soubor'
zipCompressedFile: 'ZIP komprimovaný {ext} soubor',
copyName: 'Kopírovat název',
search: 'Hledat',
title: 'Titulek',
archive: 'Archivovat', // verb
undo: 'Zpět',
moveTo: 'Přesunout do'
},
connection: { // Database connection
connection: 'Připojení',
connectionName: 'Název',
hostName: 'Host',
client: 'Klient',
@@ -75,9 +93,9 @@ export const csCZ = {
password: 'Heslo',
credentials: 'Pověření',
connect: 'Připojit',
connected: 'Připojeno', // Lawondyss: Not used
connected: 'Připojeno',
disconnect: 'Odpojit',
disconnected: 'Odpojeno', // Lawondyss: Not used
disconnected: 'Odpojeno',
ssl: 'SSL',
enableSsl: 'Použít SSL',
privateKey: 'Soukromý klíč',
@@ -90,28 +108,30 @@ export const csCZ = {
enableSsh: 'Použít SSH',
connectionString: 'String připojení',
addConnection: 'Add connection',
createConnection: 'Vytvořit připojení', // Lawondyss: Not used
createConnection: 'Vytvořit připojení',
createNewConnection: 'Vytvořit nové připojení',
askCredentials: 'Vyžadovat přihlášení',
testConnection: 'Vyzkoušet',
editConnection: 'Upravit připojení', // Lawondyss: Not used
editConnection: 'Upravit připojení',
deleteConnection: 'Smazat připojení',
connectionSuccessfullyMade: 'Spojení úspěšně navázáno!',
readOnlyMode: 'Pouze pro čtení',
allConnections: 'Všechna připojení',
searchForConnections: 'Hledat připojení'
searchForConnections: 'Hledat připojení',
keepAliveInterval: 'Keep alive interval',
singleConnection: 'Jediné spojení'
},
database: { // Database related terms
schema: 'Schéma',
type: 'Typ',
insert: 'Vložit', // Lawondyss: Not used
insert: 'Vložit',
indexes: 'Indexy',
foreignKeys: 'Cizí klíče',
length: 'Délka',
unsigned: 'Unsigned',
default: 'Výchozí',
comment: 'Komentář',
key: 'Klíč | Klíče', // Lawondyss: Used only 2
key: 'Klíč | Klíče',
order: 'Pořadí',
expression: 'Výraz',
autoIncrement: 'Auto Increment',
@@ -119,8 +139,9 @@ export const csCZ = {
field: 'Sloupec | Sloupce',
approximately: 'Přibližně',
total: 'Celkem',
table: 'Tabulka | Tabulky', // Lawondyss: Used only without argument
view: 'Pohled | Pohledy', // Lawondyss: Used only without argument
table: 'Tabulka | Tabulky',
view: 'Pohled | Pohledy',
materializedview: 'Materializovaný pohled',
definer: 'Definér',
algorithm: 'Algoritmus',
trigger: 'Trigger | Triggery',
@@ -147,25 +168,25 @@ export const csCZ = {
row: 'Řádek | Řádky',
cell: 'Buňka | Buňky',
triggerFunction: 'Trigger funkce | Trigger funkce',
routine: 'Routina | Routiny', // Lawondyss: Not used
routine: 'Routina | Routiny',
drop: 'Drop',
commit: 'Commit',
rollback: 'Rollback',
ddl: 'DDL',
collation: 'Porovnání',
resultsTable: 'Tabulka výsledků',
unableEditFieldWithoutPrimary: 'Nelze upravit buňku bez primárního klíče ve výsledku', // Lawondyss: Not used
editCell: 'Upravit buňku', // Lawondyss: Not used
unableEditFieldWithoutPrimary: 'Nelze upravit buňku bez primárního klíče ve výsledku',
editCell: 'Upravit buňku',
deleteRows: 'Smazat řádek | Smazat {count} řádků',
confirmToDeleteRows: 'Skutečně chcete smazat řádek? | Skutečně chcete smazat {count} řádků?',
addNewRow: 'Přidat nový řádek', // Lawondyss: Not used
addNewRow: 'Přidat nový řádek',
numberOfInserts: 'Počet opakování',
affectedRows: 'Ovlivněno řádků',
createNewDatabase: 'Vytvořit novou databázi', // Lawondyss: Not used
databaseName: 'Název databáze', // Lawondyss: Not use
createNewDatabase: 'Vytvořit novou databázi',
databaseName: 'Název databáze',
serverDefault: 'Porovnání serveru',
deleteDatabase: 'Smazat databázi', // Lawondyss: Not used
editDatabase: 'Upravit databázi', // Lawondyss: Not used
deleteDatabase: 'Smazat databázi',
editDatabase: 'Upravit databázi',
clearChanges: 'Zrušit změny',
addNewField: 'Přidat nový sloupec',
manageIndexes: 'Správa indexů',
@@ -177,17 +198,18 @@ export const csCZ = {
deleteField: 'Smazat sloupec',
createNewIndex: 'Vytvořit nový index',
addToIndex: 'Přidat do indexu',
createNewTable: 'Vytvořit novou databázi', // Lawondyss: Not used
createNewTable: 'Vytvořit novou databázi',
emptyTable: 'Smazat obsah tabulky',
duplicateTable: 'Duplikovat tabulku',
deleteTable: 'Smazat tabulku',
exportTable: 'Exportovat tabulku',
emptyConfirm: 'Skutečně smazat obsah tabulky',
thereAreNoIndexes: 'Nemá žádné indexy',
thereAreNoForeign: 'Nemá žádné cizí klíče',
createNewForeign: 'Vytvořit nový cizí klíč',
referenceTable: 'Ref. tabulka',
referenceField: 'Ref. sloupec',
foreignFields: 'Cizí sloupce', // Lawondyss: Not used
foreignFields: 'Cizí sloupce',
invalidDefault: 'Neplatná výchozí hodnota',
onDelete: 'Při smazání',
selectStatement: 'Definice pohledu',
@@ -195,7 +217,8 @@ export const csCZ = {
sqlSecurity: 'SQL zabezpečení',
updateOption: 'Volba aktualizace',
deleteView: 'Smazat pohled',
createNewView: 'Vytvořit nový pohled', // Lawondyss: Not used
createNewView: 'Vytvořit nový pohled',
createNewMaterializedView: 'Vytvořit nový materializovaný pohled',
deleteTrigger: 'Smazat trigger',
createNewTrigger: 'Vytvořit nový trigger',
currentUser: 'Současný uživatel',
@@ -212,7 +235,7 @@ export const csCZ = {
createNewScheduler: 'Vytvořit nový scheduler',
deleteScheduler: 'Smazat scheduler',
preserveOnCompletion: 'Uchovat po dokončení',
tableFiller: 'Vyplňovač tabulky', // Lawondyss: Not used
tableFiller: 'Vyplňovač tabulky',
fakeDataLanguage: 'Jazyk pro fake data',
queryDuration: 'Doba trvání dotazu',
setNull: 'Nastavit NULL',
@@ -224,10 +247,11 @@ export const csCZ = {
editSchema: 'Upravit schéma',
deleteSchema: 'Smazat schema',
noSchema: 'Bez schématu',
runQuery: 'Spustit dotaz', // Lawondyss: Not used
runQuery: 'Spustit dotaz',
thereAreNoTableFields: 'Nemá žádné sloupce',
newTable: 'Nová tabulka',
newView: 'Nový pohled',
newMaterializedView: 'Nový materializovaný pohled',
newTrigger: 'Nový trigger',
newRoutine: 'Nová routina',
newFunction: 'Nová funkce',
@@ -244,17 +268,17 @@ export const csCZ = {
writingTableExport: 'Zapisuji data tabulky {table}',
checkAllTables: 'Vybrat všechny tabulky',
uncheckAllTables: 'Vybrat žádnou tabulku',
killQuery: 'Zabít dotaz', // Lawondyss: Not used
insertRow: 'Vložit řádek | Vložit řádky', // Lawondyss: Used only 2
killQuery: 'Zabít dotaz',
insertRow: 'Vložit řádek | Vložit řádky',
commitMode: 'Způsob commitování',
autoCommit: 'Auto commit',
manualCommit: 'Ruční commit',
importQueryErrors: 'Varování: došlo k chybě {n} | Varování: došlo k chybám {n}', // Lawondyss: Used without n argument
executedQueries: '{n} dotaz spuštěn | {n} dotazy spuštěny', // Lawondyss: Used withoum n argument
importQueryErrors: 'Varování: došlo k chybě {n} | Varování: došlo k chybám {n}',
executedQueries: '{n} dotaz spuštěn | {n} dotazy spuštěny',
disableFKChecks: 'Vypnout kontrolu cizích klíčů',
formatQuery: 'Formátovat dotaz', // Lawondyss: Not used, probably duplicate to general.format
queryHistory: 'Historie dotazů', // Lawondyss: Not used, probably duplicate to general.history
clearQuery: 'Clear query', // Lawondyss: Not used, probably duplicate to general.clear
formatQuery: 'Formátovat dotaz',
queryHistory: 'Historie dotazů',
clearQuery: 'Clear query',
fillCell: 'Vyplnit buňku',
executeSelectedQuery: 'Spustit vybraný dotaz',
noResultsPresent: 'Nejsou k dispozici žádné výsledky',
@@ -262,12 +286,11 @@ export const csCZ = {
targetTable: 'Cílová tabulka',
switchDatabase: 'Přepnout databázi',
searchForElements: 'Vyhledávání prvků',
searchForSchemas: 'Vyhledávání schémat'
searchForSchemas: 'Vyhledávání schémat',
savedQueries: 'Uložit dotazy'
},
application: { // Application related terms
settings: 'Nastavení',
scratchpad: 'Zápisník',
disableScratchpad: 'Vypnout zápisník',
console: 'Konzole',
general: 'Obecné',
themes: 'Motivy',
@@ -275,7 +298,7 @@ export const csCZ = {
about: 'Informace',
language: 'Jazyk',
shortcuts: 'Zkratky',
key: 'Klávesa | Klávesy', // Keyboard key // Lawondyss: Usedn only 2
key: 'Klávesa | Klávesy', // Keyboard key
event: 'Akce',
light: 'Světlý',
dark: 'Tmavý',
@@ -283,13 +306,19 @@ export const csCZ = {
application: 'Aplikace',
editor: 'Editor',
changelog: 'Changelog',
small: 'Malé', // Lawondyss: Not used, probably obsolete font size settings
medium: 'Střední', // Lawondyss: Not used, probably obsolete font size settings
large: 'Velké', // Lawondyss: Not used, probably obsolete font size settings
small: 'Malé',
medium: 'Střední',
large: 'Velké',
appearance: 'Vzhled',
color: 'Barva',
label: 'Název',
icon: 'Ikona',
customIcon: 'Vlastní ikona',
fileName: 'Soubor',
choseFile: 'Vybrat soubor',
data: 'Data',
password: 'Heslo',
required: 'Povinné',
madeWithJS: 'Vytvořeno s 💛 a JavaScriptem!',
checkForUpdates: 'Zkontrolovat aktualizace',
noUpdatesAvailable: 'Žádné dostupné aktualizace',
@@ -308,7 +337,7 @@ export const csCZ = {
editorTheme: 'Motiv editoru',
wrapLongLines: 'Zalamovat dlouhé řádky',
markdownSupported: 'Podporován Markdown',
plantATree: 'Zasaďte strom', // Lawondyss: Not used
plantATree: 'Zasaďte strom',
dataTabPageSize: 'Počet řádků na stránku',
noOpenTabs: 'Žádné otevřené karty, vyberte z elementů vlevo nebo:',
restorePreviousSession: 'Pamatovat si otevřené karty',
@@ -333,15 +362,16 @@ export const csCZ = {
saveContent: 'Uložit obsah',
openAllConnections: 'Otevřít všechna připojení',
openSettings: 'Otevřít nastavení',
openScratchpad: 'Otevřít zápisník',
runOrReload: 'Spustit dotaz',
openFilter: 'Otevřít filtr',
nextResultsPage: 'Další stránka výsledků',
previousResultsPage: 'Předešlá stránka výsledků',
editFolder: 'Upravit složku',
folderName: 'Název složky',
deleteFolder: 'Smazat složku', // Lawondyss: Not used
editConnectionAppearance: 'Upravit vzhled připojení', // Lawondyss: Not used
deleteFolder: 'Smazat složku',
newFolder: 'Nová složka',
outOfFolder: 'Mimo složku',
editConnectionAppearance: 'Upravit vzhled připojení',
defaultCopyType: 'Výchozí typ kopírování',
showTableSize: 'Velikost tabulky v panelu',
showTableSizeDescription: 'Pouze MySQL/MariaDB. Povolení této možnosti může ovlivnit výkon u schémat s mnoha tabulkami.',
@@ -356,7 +386,33 @@ export const csCZ = {
csvStringDelimiter: 'Obalit text',
csvIncludeHeader: 'Včetně hlavičky',
csvExportOptions: 'Možnosti CSV exportu',
scratchPadDefaultValue: '# JAK PODPOŘIT ANTARES\n\n- [ ] Dát Antares hvězdičku [GitHub repo](https://github.com/antares-sql/antares)\n- [ ] Poslat názor či radu\n- [ ] Nahlásit chybu\n- [ ] Pokud se líbí, sdílet Antares s přáteli\n\n# O ZÁPISNÍKU\n\nToto je zápisník, který uchovává vaše **osobní poznámky**. Podporuje `markdown` formát, ale můžete použít obyčejný text.\nTento obsah je pouze ukázky, neváhejte ho smazat, abyste si udělali místo na poznámky.\n'
exportData: 'Exportovat data',
exportDataExplanation: 'Export uložených připojení v Antaresu. Budete požádáni o zadání hesla pro zašifrování exportovaného souboru.',
importData: 'Importovat data',
importDataExplanation: 'Importuje soubor .antares obsahující připojení. Je třeba zadat heslo definované při exportu.',
includeConnectionPasswords: 'Včetně hesel připojení',
includeFolders: 'Včetně složek',
encryptionPassword: 'Heslo pro zašifrování souboru',
encryptionPasswordError: 'Heslo musí mít alespoň 8 znaků.',
ignoreDuplicates: 'Ignorovat duplicity',
wrongImportPassword: 'Chybné heslo pro import',
wrongFileFormat: 'Chybný formát souboru',
dataImportSuccess: 'Data úspěšně importována',
note: 'Poznámka',
thereAreNoNotesYet: 'Zatím tu nejsou žádné poznámky',
addNote: 'Přidat poznámku',
editNote: 'Upravit poznámku',
saveAsNote: 'Uložit jako poznámku',
showArchivedNotes: 'Zobrazit archivované poznámky',
hideArchivedNotes: 'Skrýt archivované poznámky',
tag: 'Tag', // Note tag
saveFile: 'Uložit soubor',
saveFileAs: 'Uložit do nového souboru',
openFile: 'Otevřít soubor',
openNotes: 'Otevřít poznámky',
debugConsole: 'Debug konzole', // <- console tab name
executedQueries: 'Log dotazů', // <- console tab name
sizeLimitError: 'Maximální velikost {size} překročena'
},
faker: { // Faker.js methods, used in random generated content
address: 'Address',

582
src/renderer/i18n/he-IL.ts Normal file
View File

@@ -0,0 +1,582 @@
/**
* [TRANSLATION UPDATE HELPER]
* - Open a terminal in antares folder and run `npm run translation:check short-code` replacing short-code with the one you are updating.
* - The command will output which terms are missing or not translated from english.
* - Open antares folder with your editor of choice.
* - Go to antares/src/renderer/i18n/ and open the locale file you want to translate.
* - Add and translate missing terms and consider whether to translate untranslated terms.
*/
export const heIL = {
general: { // General purpose terms
edit: 'עריכה',
save: 'שמירה',
close: 'סגירה',
delete: 'מחיקה',
confirm: 'אישור',
cancel: 'ביטול',
send: 'שליחה',
refresh: 'רענון',
autoRefresh: 'רענון אוטומטי',
version: 'גרסה',
donate: 'תרומה',
run: 'הרצה',
results: 'תוצאות',
size: 'גודל',
mimeType: 'סוג MIME',
download: 'הורדה',
add: 'הוספה',
data: 'נתונים',
properties: 'מאפיינים',
name: 'שם',
clear: 'ניקוי',
options: 'אפשרויות',
insert: 'הכנסה',
discard: 'ביטול',
stay: 'הישאר',
author: 'מחבר',
upload: 'העלאה',
browse: 'עיון',
content: 'תוכן',
cut: 'גזירה',
copy: 'העתקה',
paste: 'הדבקה',
duplicate: 'שכפול',
tools: 'כלים',
seconds: 'שניות',
all: 'הכל',
new: 'חדש',
select: 'בחירה',
change: 'שינוי',
include: 'כלול',
includes: 'כולל',
completed: 'הושלם',
aborted: 'בוטל',
disabled: 'מושבת',
enable: 'הפעל',
disable: 'השבת',
contributors: 'תורמים',
pin: 'נעץ',
unpin: 'בטל נעיצה',
folder: 'תיקייה | תיקיות',
none: 'אין',
singleQuote: 'מרכאה בודדת',
doubleQuote: 'מרכאות כפולות',
deleteConfirm: 'האם אתה מאשר את הביטול של',
uploadFile: 'העלאת קובץ',
format: 'פורמט', // Format code
history: 'היסטוריה',
filter: 'סינון',
manualValue: 'ערך ידני',
selectAll: 'בחר הכל',
pageNumber: 'מספר עמוד',
directoryPath: 'נתיב תיקייה',
actionSuccessful: '{action} בוצעה בהצלחה',
outputFormat: 'פורמט פלט',
singleFile: 'קובץ {ext} בודד',
zipCompressedFile: 'קובץ {ext} דחוס ב-ZIP',
copyName: 'העתק שם',
search: 'חיפוש',
title: 'כותרת',
archive: 'ארכיון', // verb
undo: 'ביטול פעולה',
moveTo: 'העבר אל'
},
connection: { // Database connection
connection: 'חיבור',
connectionName: 'שם החיבור',
hostName: 'שם המארח',
client: 'לקוח',
port: 'פורט',
user: 'משתמש',
password: 'סיסמה',
credentials: 'אישורים',
connect: 'התחבר',
connected: 'מחובר',
disconnect: 'התנתק',
disconnected: 'מנותק',
ssl: 'SSL',
enableSsl: 'הפעל SSL',
privateKey: 'מפתח פרטי',
certificate: 'תעודה',
caCertificate: 'תעודת CA',
ciphers: 'צפנים',
untrustedConnection: 'חיבור לא מהימן',
passphrase: 'ביטוי סיסמה',
sshTunnel: 'מנהרת SSH',
enableSsh: 'הפעל SSH',
connectionString: 'מחרוזת חיבור',
addConnection: 'הוסף חיבור',
createConnection: 'צור חיבור',
createNewConnection: 'צור חיבור חדש',
askCredentials: 'בקש אישורים',
testConnection: 'בדוק חיבור',
editConnection: 'ערוך חיבור',
deleteConnection: 'מחק חיבור',
connectionSuccessfullyMade: 'החיבור בוצע בהצלחה!',
readOnlyMode: 'מצב קריאה בלבד',
allConnections: 'כל החיבורים',
searchForConnections: 'חפש חיבורים',
keepAliveInterval: 'מרווח שמירת חיבור',
singleConnection: 'חיבור בודד'
},
database: { // Database related terms
schema: 'סכימה',
type: 'סוג',
insert: 'הכנס',
indexes: 'אינדקסים',
foreignKeys: 'מפתחות זרים',
length: 'אורך',
unsigned: 'ללא סימן',
default: 'ברירת מחדל',
comment: 'הערה',
key: 'מפתח | מפתחות',
order: 'סדר',
expression: 'ביטוי',
autoIncrement: 'מספור אוטומטי',
engine: 'מנוע',
field: 'שדה | שדות',
approximately: 'בקירוב',
total: 'סך הכל',
table: 'טבלה | טבלאות',
view: 'תצוגה | תצוגות',
materializedview: 'תצוגה ממומשת | תצוגות ממומשות',
definer: 'מגדיר',
algorithm: 'אלגוריתם',
trigger: 'טריגר | טריגרים',
storedRoutine: 'שגרה שמורה | שגרות שמורות',
scheduler: 'מתזמן | מתזמנים',
event: 'אירוע',
parameters: 'פרמטרים',
function: 'פונקציה | פונקציות',
deterministic: 'דטרמיניסטי',
context: 'הקשר',
export: 'ייצוא',
import: 'ייבוא',
returns: 'מחזיר',
timing: 'תזמון',
state: 'מצב',
execution: 'ביצוע',
starts: 'מתחיל',
ends: 'מסתיים',
variables: 'משתנים',
processes: 'תהליכים',
database: 'מסד נתונים',
array: 'מערך',
structure: 'מבנה',
row: 'שורה | שורות',
cell: 'תא | תאים',
triggerFunction: 'פונקציית טריגר | פונקציות טריגר',
routine: 'שגרה | שגרות',
drop: 'הסר',
commit: 'בצע',
rollback: 'שחזר',
ddl: 'DDL',
collation: 'אוסף',
resultsTable: 'טבלת תוצאות',
unableEditFieldWithoutPrimary: 'לא ניתן לערוך שדה ללא מפתח ראשי בתוצאות',
editCell: 'ערוך תא',
deleteRows: 'מחק שורה | מחק {count} שורות',
confirmToDeleteRows: 'האם אתה מאשר למחוק שורה אחת? | האם אתה מאשר למחוק {count} שורות?',
addNewRow: 'הוסף שורה חדשה',
numberOfInserts: 'מספר הכנסות',
affectedRows: 'שורות מושפעות',
createNewDatabase: 'צור מסד נתונים חדש',
databaseName: 'שם מסד הנתונים',
serverDefault: 'ברירת מחדל של השרת',
deleteDatabase: 'מחק מסד נתונים',
editDatabase: 'ערוך מסד נתונים',
clearChanges: 'נקה שינויים',
addNewField: 'הוסף שדה חדש',
manageIndexes: 'נהל אינדקסים',
manageForeignKeys: 'נהל מפתחות זרים',
allowNull: 'אפשר NULL',
zeroFill: 'מילוי אפסים',
customValue: 'ערך מותאם אישית',
onUpdate: 'בעת עדכון',
deleteField: 'מחק שדה',
createNewIndex: 'צור אינדקס חדש',
addToIndex: 'הוסף לאינדקס',
createNewTable: 'צור טבלה חדשה',
emptyTable: 'רוקן טבלה',
duplicateTable: 'שכפל טבלה',
deleteTable: 'מחק טבלה',
exportTable: 'ייצא טבלה',
emptyConfirm: 'האם אתה מאשר לרוקן',
thereAreNoIndexes: 'אין אינדקסים',
thereAreNoForeign: 'אין מפתחות זרים',
createNewForeign: 'צור מפתח זר חדש',
referenceTable: 'טבלת התייחסות',
referenceField: 'שדה התייחסות',
foreignFields: 'שדות זרים',
invalidDefault: 'ברירת מחדל לא חוקית',
onDelete: 'בעת מחיקה',
selectStatement: 'הצהרת SELECT',
triggerStatement: 'הצהרת טריגר',
sqlSecurity: 'אבטחת SQL',
updateOption: 'אפשרות עדכון',
deleteView: 'מחק תצוגה',
createNewView: 'צור תצוגה חדשה',
createNewMaterializedView: 'צור תצוגה ממומשת חדשה',
deleteTrigger: 'מחק טריגר',
createNewTrigger: 'צור טריגר חדש',
currentUser: 'משתמש נוכחי',
routineBody: 'גוף השגרה',
dataAccess: 'גישה לנתונים',
thereAreNoParameters: 'אין פרמטרים',
createNewParameter: 'צור פרמטר חדש',
createNewRoutine: 'צור שגרה שמורה חדשה',
deleteRoutine: 'מחק שגרה שמורה',
functionBody: 'גוף הפונקציה',
createNewFunction: 'צור פונקציה חדשה',
deleteFunction: 'מחק פונקציה',
schedulerBody: 'גוף המתזמן',
createNewScheduler: 'צור מתזמן חדש',
deleteScheduler: 'מחק מתזמן',
preserveOnCompletion: 'שמור בסיום',
tableFiller: 'ממלא טבלאות',
fakeDataLanguage: 'שפת נתונים מזויפים',
queryDuration: 'משך השאילתה',
setNull: 'הגדר NULL',
processesList: 'רשימת תהליכים',
processInfo: 'מידע על תהליך',
manageUsers: 'נהל משתמשים',
createNewSchema: 'צור סכימה חדשה',
schemaName: 'שם הסכימה',
editSchema: 'ערוך סכימה',
deleteSchema: 'מחק סכימה',
noSchema: 'אין סכימה',
runQuery: 'הרץ שאילתה',
thereAreNoTableFields: 'אין שדות בטבלה',
newTable: 'טבלה חדשה',
newView: 'תצוגה חדשה',
newMaterializedView: 'תצוגה ממומשת חדשה',
newTrigger: 'טריגר חדש',
newRoutine: 'שגרה חדשה',
newFunction: 'פונקציה חדשה',
newScheduler: 'מתזמן חדש',
newTriggerFunction: 'פונקציית טריגר חדשה',
thereAreNoQueriesYet: 'אין עדיין שאילתות',
searchForQueries: 'חפש שאילתות',
killProcess: 'סיים תהליך',
exportSchema: 'ייצא סכ',
importSchema: 'ייבא סכימה',
newInsertStmtEvery: 'הצהרת INSERT חדשה כל',
processingTableExport: 'מעבד {table}',
fetchingTableExport: 'מביא נתוני {table}',
writingTableExport: 'כותב נתוני {table}',
checkAllTables: 'סמן את כל הטבלאות',
uncheckAllTables: 'בטל סימון כל הטבלאות',
killQuery: 'הרוג שאילתה',
insertRow: 'הכנס שורה | הכנס שורות',
commitMode: 'מצב ביצוע',
autoCommit: 'ביצוע אוטומטי',
manualCommit: 'ביצוע ידני',
importQueryErrors: 'אזהרה: אירעה {n} שגיאה | אזהרה: אירעו {n} שגיאות',
executedQueries: 'בוצעה {n} שאילתה | בוצעו {n} שאילתות',
disableFKChecks: 'בטל בדיקות מפתח זר',
formatQuery: 'עצב שאילתה',
queryHistory: 'היסטוריית שאילתות',
clearQuery: 'נקה שאילתה',
fillCell: 'מלא תא',
executeSelectedQuery: 'בצע שאילתה נבחרת',
noResultsPresent: 'אין תוצאות',
sqlExportOptions: 'אפשרויות ייצוא SQL',
targetTable: 'טבלת יעד',
switchDatabase: 'החלף מסד נתונים',
searchForElements: 'חפש אלמנטים',
searchForSchemas: 'חפש סכימות',
savedQueries: 'שאילתות שמורות'
},
application: { // Application related terms
settings: 'הגדרות',
console: 'קונסולה',
general: 'כללי',
themes: 'ערכות נושא',
update: 'עדכון',
about: 'אודות',
language: 'שפה',
shortcuts: 'קיצורי דרך',
key: 'מקש | מקשים', // Keyboard key
event: 'אירוע',
light: 'בהיר',
dark: 'כהה',
autoCompletion: 'השלמה אוטומטית',
application: 'יישום',
editor: 'עורך',
changelog: 'יומן שינויים',
small: 'קטן',
medium: 'בינוני',
large: 'גדול',
appearance: 'מראה',
color: 'צבע',
label: 'תווית',
icon: 'סמל',
customIcon: 'סמל מותאם אישית',
fileName: 'שם קובץ',
choseFile: 'בחר קובץ',
data: 'נתונים',
password: 'סיסמה',
required: 'נדרש',
madeWithJS: 'נוצר עם 💛 ו-JavaScript!',
checkForUpdates: 'בדוק עדכונים',
noUpdatesAvailable: 'אין עדכונים זמינים',
checkingForUpdate: 'בודק עדכונים',
checkFailure: 'הבדיקה נכשלה, נסה שוב מאוחר יותר',
updateAvailable: 'עדכון זמין',
downloadingUpdate: 'מוריד עדכון',
updateDownloaded: 'העדכון הורד',
restartToInstall: 'הפעל מחדש את Antares כדי להתקין',
includeBetaUpdates: 'כלול עדכוני בטא',
notificationsTimeout: 'זמן התראות',
openNewTab: 'פתח כרטיסייה חדשה',
unsavedChanges: 'שינויים שלא נשמרו',
discardUnsavedChanges: 'יש לך שינויים שלא נשמרו. סגירת כרטיסייה זו תגרום לאובדן השינויים.',
applicationTheme: 'ערכת נושא ליישום',
editorTheme: 'ערכת נושא לעורך',
wrapLongLines: 'גלישת שורות ארוכות',
markdownSupported: 'תמיכה ב-Markdown',
plantATree: 'נטע עץ',
dataTabPageSize: 'תוצאות לעמוד',
noOpenTabs: 'אין כרטיסיות פתוחות, נווט בסרגל השמאלי או:',
restorePreviousSession: 'שחזר הפעלה קודמת',
closeTab: 'סגור כרטיסייה',
goToDownloadPage: 'עבור לדף ההורדה',
disableBlur: 'בטל טשטוש',
missingOrIncompleteTranslation: 'תרגום חסר או לא שלם?',
findOutHowToContribute: 'גלה כיצד לתרום',
reportABug: 'דווח על באג',
nextTab: 'כרטיסייה הבאה',
previousTab: 'כרטיסייה קודמת',
selectTabNumber: 'בחר כרטיסייה מספר {param}',
toggleConsole: 'הצג/הסתר קונסולה',
addShortcut: 'הוסף קיצור דרך',
editShortcut: 'ערוך קיצור דרך',
deleteShortcut: 'מחק קיצור דרך',
restoreDefaults: 'שחזר ברירות מחדל',
restoreDefaultsQuestion: 'האם אתה מאשר לשחזר את ערכי ברירת המחדל?',
registerAShortcut: 'רשום קיצור דרך',
invalidShortcutMessage: 'שילוב לא חוקי, המשך להקליד',
shortcutAlreadyExists: 'קיצור הדרך כבר קיים',
saveContent: 'שמור תוכן',
openAllConnections: 'פתח את כל החיבורים',
openSettings: 'פתח הגדרות',
runOrReload: 'הרץ או טען מחדש',
openFilter: 'פתח מסנן',
nextResultsPage: 'עמוד תוצאות הבא',
previousResultsPage: 'עמוד תוצאות קודם',
editFolder: 'ערוך תיקייה',
folderName: 'שם תיקייה',
deleteFolder: 'מחק תיקייה',
newFolder: 'תיקייה חדשה',
outOfFolder: 'מחוץ לתיקייה',
editConnectionAppearance: 'ערוך מראה חיבור',
defaultCopyType: 'סוג העתקה ברירת מחדל',
showTableSize: 'הצג גודל טבלה בסרגל הצד',
showTableSizeDescription: 'MySQL/MariaDB בלבד. הפעלת אפשרות זו עלולה להשפיע על הביצועים בסכימה עם טבלאות רבות.',
switchSearchMethod: 'החלף שיטת חיפוש',
phpArray: 'מערך PHP',
closeAllTabs: 'סגור את כל הכרטיסיות',
closeOtherTabs: 'סגור כרטיסיות אחרות',
closeTabsToLeft: 'סגור כרטיסיות משמאל',
closeTabsToRight: 'סגור כרטיסיות מימין',
csvFieldDelimiter: 'מפריד שדות',
csvLinesTerminator: 'מסיים שורות',
csvStringDelimiter: 'מפריד מחרוזות',
csvIncludeHeader: 'כלול כותרת',
csvExportOptions: 'אפשרויות ייצוא CSV',
exportData: 'ייצא נתונים',
exportDataExplanation: 'ייצא חיבורים שמורים ל-Antares. תתבקש להזין סיסמה להצפנת הקובץ המיוצא.',
importData: 'ייבא נתונים',
importDataExplanation: 'מייבא קובץ .antares המכיל חיבורים. תצטרך להזין את הסיסמה שהוגדרה בזמן הייצוא.',
includeConnectionPasswords: 'כלול סיסמאות חיבור',
includeFolders: 'כלול תיקיות',
encryptionPassword: 'סיסמת הצפנה',
encryptionPasswordError: 'סיסמת ההצפנה חייבת להיות באורך של 8 תווים לפחות.',
ignoreDuplicates: 'התעלם מכפילויות',
wrongImportPassword: 'סיסמת ייבוא שגויה',
wrongFileFormat: 'פורמט קובץ שגוי',
dataImportSuccess: 'הנתונים יובאו בהצלחה',
note: 'הערה | הערות',
thereAreNoNotesYet: 'אין עדיין הערות',
addNote: 'הוסף הערה',
editNote: 'ערוך הערה',
saveAsNote: 'שמור כהערה',
showArchivedNotes: 'הצג הערות בארכיון',
hideArchivedNotes: 'הסתר הערות בארכיון',
tag: 'תג', // Note tag
saveFile: 'שמור קובץ',
saveFileAs: 'שמור קובץ בשם',
openFile: 'פתח קובץ',
openNotes: 'פתח הערות',
debugConsole: 'קונסולת ניפוי', // <- console tab name
executedQueries: 'שאילתות שבוצעו', // <- console tab name
sizeLimitError: 'חריגה מהגודל המקסימלי של {size}'
},
faker: { // Faker.js methods, used in random generated content
address: 'כתובת',
commerce: 'מסחר',
company: 'חברה',
database: 'מסד נתונים',
date: 'תאריך',
finance: 'פיננסים',
git: 'Git',
hacker: 'האקר',
internet: 'אינטרנט',
lorem: 'לורם',
name: 'שם',
music: 'מוזיקה',
phone: 'טלפון',
random: 'אקראי',
system: 'מערכת',
time: 'זמן',
vehicle: 'רכב',
zipCode: 'מיקוד',
zipCodeByState: 'מיקוד לפי מדינה',
city: 'עיר',
cityPrefix: 'קידומת עיר',
citySuffix: 'סיומת עיר',
streetName: 'שם רחוב',
streetAddress: 'כתובת רחוב',
streetSuffix: 'סיומת רחוב',
streetPrefix: 'קידומת רחוב',
secondaryAddress: 'כתובת משנית',
county: 'מחוז',
country: 'מדינה',
countryCode: 'קוד מדינה',
state: 'מדינה',
stateAbbr: 'קיצור מדינה',
latitude: 'קו רוחב',
longitude: 'קו אורך',
direction: 'כיוון',
cardinalDirection: 'כיוון קרדינלי',
ordinalDirection: 'כיוון אורדינלי',
nearbyGPSCoordinate: 'קואורדינטת GPS קרובה',
timeZone: 'אזור זמן',
color: 'צבע',
department: 'מחלקה',
productName: 'שם מוצר',
price: 'מחיר',
productAdjective: 'תואר מוצר',
productMaterial: 'חומר מוצר',
product: 'מוצר',
productDescription: 'תיאור מוצר',
suffixes: 'סיומות',
companyName: 'שם חברה',
companySuffix: 'סיומת חברה',
catchPhrase: 'סיסמה',
bs: 'BS',
catchPhraseAdjective: 'תואר סיסמה',
catchPhraseDescriptor: 'מתאר סיסמה',
catchPhraseNoun: 'שם עצם סיסמה',
bsAdjective: 'תואר BS',
bsBuzz: 'באז BS',
bsNoun: 'שם עצם BS',
column: 'עמודה',
type: 'סוג',
collation: 'קולציה',
engine: 'מנוע',
past: 'עבר',
now: 'עכשיו',
future: 'עתיד',
between: 'בין',
recent: 'לאחרונה',
soon: 'בקרוב',
month: 'חודש',
weekday: 'יום בשבוע',
account: 'חשבון',
accountName: 'שם החשבון',
routingNumber: 'מספר ניתוב',
mask: 'מסכה',
amount: 'סכום',
transactionType: 'סוג העסקה',
currencyCode: 'קוד מטבע',
currencyName: 'שם המטבע',
currencySymbol: 'סמל המטבע',
bitcoinAddress: 'כתובת ביטקוין',
litecoinAddress: 'כתובת לייטקוין',
creditCardNumber: 'מספר כרטיס אשראי',
creditCardCVV: 'CVV של כרטיס אשראי',
ethereumAddress: 'כתובת אתריום',
iban: 'איבן',
bic: 'BIC',
transactionDescription: 'תיאור העסקה',
branch: 'סניף',
commitEntry: 'ערך קומיט',
commitMessage: 'הודעת קומיט',
commitSha: 'SHA של קומיט',
shortSha: 'SHA קצר',
abbreviation: 'קיצור',
adjective: 'שם תואר',
noun: 'שם עצם',
verb: 'פועל',
ingverb: 'פועל בצורת -ing',
phrase: 'ביטוי',
avatar: 'אווטאר',
email: 'אימייל',
exampleEmail: 'דוגמת אימייל',
userName: 'שם משתמש',
protocol: 'פרוטוקול',
url: 'כתובת URL',
domainName: 'שם דומיין',
domainSuffix: 'סיומת דומיין',
domainWord: 'מילת דומיין',
ip: 'IP',
ipv6: 'IPv6',
userAgent: 'User Agent',
mac: 'כתובת MAC',
password: 'סיסמה',
word: 'מילה',
words: 'מילים',
sentence: 'משפט',
slug: 'סלאג',
sentences: 'משפטים',
paragraph: 'פסקה',
paragraphs: 'פסקאות',
text: 'טקסט',
lines: 'שורות',
genre: 'ז\'אנר',
firstName: 'שם פרטי',
lastName: 'שם משפחה',
middleName: 'שם אמצעי',
findName: 'שם מלא',
jobTitle: 'תפקיד',
gender: 'מין',
prefix: 'תחילית',
suffix: 'סיומת',
title: 'כותרת',
jobDescriptor: 'תיאור תפקיד',
jobArea: 'תחום תפקיד',
jobType: 'סוג תפקיד',
phoneNumber: 'מספר טלפון',
phoneNumberFormat: 'פורמט מספר טלפון',
phoneFormats: 'פורמטים של מספר טלפון',
number: 'מספר',
float: 'מספר עשרוני',
arrayElement: 'אלמנט במערך',
arrayElements: 'אלמנטים במערך',
objectElement: 'אלמנט באובייקט',
uuid: 'UUID',
boolean: 'בוליאני',
image: 'תמונה',
locale: 'לוקאל',
alpha: 'אלפא',
alphaNumeric: 'אלפאנומרי',
hexaDecimal: 'הקסדצימלי',
fileName: 'שם קובץ',
commonFileName: 'שם קובץ נפוץ',
mimeType: 'סוג MIME',
commonFileType: 'סוג קובץ נפוץ',
commonFileExt: 'סיומת קובץ נפוצה',
fileType: 'סוג קובץ',
fileExt: 'סיומת קובץ',
directoryPath: 'נתיב תיקייה',
filePath: 'נתיב קובץ',
semver: 'גרסת Semver',
manufacturer: 'יצרן',
model: 'דגם',
fuel: 'דלק',
vin: 'מספר רכב (VIN)'
}
};

View File

@@ -7,6 +7,7 @@ import { deDE } from './de-DE';
import { enUS } from './en-US';
import { esES } from './es-ES';
import { frFR } from './fr-FR';
import { heIL } from './he-IL';
import { idID } from './id-ID';
import { itIT } from './it-IT';
import { jaJP } from './ja-JP';
@@ -17,6 +18,8 @@ import { ruRU } from './ru-RU';
import { ukUA } from './uk-UA';
import { viVN } from './vi-VN';
import { zhCN } from './zh-CN';
import { zhTW } from './zh-TW';
const messages = {
'en-US': enUS,
'it-IT': itIT,
@@ -34,7 +37,9 @@ const messages = {
'nl-NL': nlNL,
'ca-ES': caES,
'cs-CZ': csCZ,
'uk-UA': ukUA
'uk-UA': ukUA,
'zh-TW': zhTW,
'he-IL': heIL
};
type NestedPartial<T> = {

View File

@@ -9,11 +9,13 @@ export const localesNames: Record<string, string> = {
'vi-VN': 'Tiếng Việt',
'ja-JP': '日本語',
'zh-CN': '简体中文',
'zh-TW': '正體中文',
'ru-RU': 'Русский',
'id-ID': 'Bahasa Indonesia',
'ko-KR': '한국어',
'nl-NL': 'Nederlands',
'ca-ES': 'Català',
'cs-CZ': 'Čeština',
'uk-UA': 'Українська'
'uk-UA': 'Українська',
'he-IL': 'עברית'
};

569
src/renderer/i18n/zh-TW.ts Normal file
View File

@@ -0,0 +1,569 @@
export const zhTW = {
general: {
// 通用術語
edit: '編輯',
save: '保存',
close: '關閉',
delete: '刪除',
confirm: '確定',
cancel: '取消',
send: '發送',
refresh: '重新整理',
autoRefresh: '自動重新整理',
version: '版本',
donate: '捐贈',
run: '執行',
results: '結果',
size: '大小',
mimeType: 'MIME類型',
download: '下載',
add: '新增',
data: '資料',
properties: '屬性',
name: '名稱',
clear: '清除',
options: '選項',
insert: '插入',
discard: '丟棄',
stay: '等待',
author: '作者',
upload: '上傳',
browse: '瀏覽',
content: '內容',
cut: '剪下',
copy: '複製',
paste: '貼上',
duplicate: '副本',
tools: '工具',
seconds: '秒',
all: '全部',
new: '新',
select: '選擇',
change: '變更',
include: '包含',
includes: '包含',
completed: '已完成',
aborted: '中止',
disabled: '已禁用',
enable: '啟用',
disable: '禁用',
contributors: '貢獻者',
pin: '固定',
unpin: '取消固定',
folder: '檔案夾 | 檔案夾',
none: '無',
singleQuote: '單引號',
doubleQuote: '雙引號',
deleteConfirm: '您是否確認取消',
uploadFile: '上傳檔案',
format: '格式碼', // 格式碼
history: '曆史',
filter: '過濾器',
manualValue: '手動值',
selectAll: '選擇全部',
pageNumber: '頁數',
directoryPath: '目錄路徑',
actionSuccessful: '{action} 成功',
outputFormat: '輸出格式',
singleFile: '單個 {ext} 檔案',
zipCompressedFile: 'ZIP 壓縮 {ext} 檔案',
copyName: '複製名稱',
search: '搜索',
title: '標題',
archive: '封存',
undo: '重做',
moveTo: '移動到'
},
connection: {
// 資料庫連接
connection: '連線',
connectionName: '連線名稱',
hostName: '主機名',
client: '資料庫類型',
port: '連線埠',
user: '使用者',
password: '密碼',
credentials: '憑證',
connect: '連線',
connected: '已連線',
disconnect: '斷開連線',
disconnected: '斷開連線',
ssl: 'SSL',
enableSsl: '啟用 SSL',
privateKey: '私鑰',
certificate: '證書',
caCertificate: 'CA 證書',
ciphers: '密碼',
untrustedConnection: '不受信任的連線',
passphrase: '密碼提示',
sshTunnel: 'SSH 通道',
enableSsh: '啟用 SSH',
connectionString: '連接字符串',
addConnection: '新增連線',
createConnection: '建立連線',
createNewConnection: '建立新連線',
askCredentials: '詢問憑據',
testConnection: '測試連線',
editConnection: '編輯連線',
deleteConnection: '刪除連線',
connectionSuccessfullyMade: '連線成功了!',
readOnlyMode: '唯讀模式',
allConnections: '所有連線',
searchForConnections: '搜索連線',
keepAliveInterval: '保持活躍間隔',
singleConnection: '單一連線'
},
database: {
// 資料庫庫相關術語
schema: '模式(schema)',
type: '類型',
insert: '插入',
indexes: '索引',
foreignKeys: '外鍵',
length: '長度',
unsigned: '無符號',
default: '預設',
comment: '註釋',
key: '鍵 | 鍵',
order: '排序',
expression: '表達式',
autoIncrement: '自動增量',
engine: '引擎',
field: '字段 | 字段',
approximately: '大約',
total: '總計',
table: '表 | 表',
view: '視圖 | 視圖',
definer: '定義者',
algorithm: '算法',
trigger: '觸發器 | 觸發器',
storedRoutine: '存儲例程 | 存儲例程',
scheduler: '調度器 | 調度器',
event: '事件',
parameters: '參數',
function: '函數 | 函數',
deterministic: '確定的',
context: '上下文',
export: '匯出',
import: '匯入',
returns: '回傳',
timing: '定時',
state: '狀態',
execution: '執行',
starts: '開始',
ends: '結束',
variables: '變數',
processes: '進程',
database: '資料庫',
array: '數據',
structure: '結構',
row: '行 | 行',
cell: '單元格 | 單元格',
triggerFunction: '觸發器函數 | 觸發器函數',
routine: '例程 | 例程',
drop: 'Drop',
commit: '提交',
rollback: '回滾',
ddl: '資料定義語言',
collation: '排序規則',
resultsTable: '結果表',
unableEditFieldWithoutPrimary: '無法編輯結果集中一個沒有主鍵的字段',
editCell: '編輯單元格',
deleteRows: '刪除行 | 刪除 {count} 行',
confirmToDeleteRows: '您是否確認要刪除一行? | 您是否確認要刪除 {count} 行?',
addNewRow: '新增行',
numberOfInserts: '插入的數量',
affectedRows: '受影響的行',
createNewDatabase: '建立新資料庫',
databaseName: '資料庫名稱',
serverDefault: '預設伺服器',
deleteDatabase: '刪除資料庫',
editDatabase: '編輯資料庫',
clearChanges: '清除變更',
addNewField: '新增新字段',
manageIndexes: '管理索引',
manageForeignKeys: '管理外鍵',
allowNull: '允許 NULL',
zeroFill: 'zeroFill',
customValue: '自定義值',
onUpdate: '在更新',
deleteField: '刪除字段',
createNewIndex: '建立新索引',
addToIndex: '新增到索引',
createNewTable: '建立新表',
emptyTable: '清空表',
duplicateTable: '重複表',
deleteTable: '刪除表',
exportTable: '導出表',
emptyConfirm: '您是否確認清空',
thereAreNoIndexes: '沒有索引',
thereAreNoForeign: '沒有外鍵',
createNewForeign: '建立新外鍵',
referenceTable: '參考表',
referenceField: '參考字段',
foreignFields: '外鍵字段',
invalidDefault: '無效預設值',
onDelete: '在刪除',
selectStatement: '選擇語句',
triggerStatement: '觸發器語句',
sqlSecurity: 'SQL 安全',
updateOption: '更新選項',
deleteView: '刪除視圖',
createNewView: '建立新視圖',
deleteTrigger: '刪除觸發器',
createNewTrigger: '建立新觸發器',
currentUser: '當前使用者',
routineBody: '例程主體',
dataAccess: '數據訪問',
thereAreNoParameters: '沒有參數',
createNewParameter: '建立新參數',
createNewRoutine: '建立新存儲例程',
deleteRoutine: '刪除存儲例程',
functionBody: '函數體',
createNewFunction: '建立新函數',
deleteFunction: '刪除函數',
schedulerBody: '調度器主體',
createNewScheduler: '建立新調度器',
deleteScheduler: '刪除調度器',
preserveOnCompletion: '完成時保留',
tableFiller: '表填充器',
fakeDataLanguage: '僞造的數據語言',
queryDuration: '查詢持續時間',
setNull: '設定 NULL',
processesList: '進程列表',
processInfo: '進程信息',
manageUsers: '管理使用者',
createNewSchema: '建立新模式(schema)',
schemaName: '模式名稱',
editSchema: '編輯模式',
deleteSchema: '刪除模式',
noSchema: '沒有模式',
runQuery: '運行查詢',
thereAreNoTableFields: '沒有表的字段',
newTable: '新表',
newView: '新視圖',
newTrigger: '新觸發器',
newRoutine: '新例程',
newFunction: '新函數',
newScheduler: '新調度器',
newTriggerFunction: '新觸發器函數',
thereAreNoQueriesYet: '目前還沒有任何查詢',
searchForQueries: '搜索查詢',
killProcess: '終止進程',
exportSchema: '導出模式(schema)',
importSchema: '導入模式(schema)',
newInsertStmtEvery: '每條新的 INSERT 語句',
processingTableExport: '處理 {table}',
fetchingTableExport: '正在獲取 {table} 數據',
writingTableExport: '正在寫入 {table} 數據',
checkAllTables: '檢查所有表',
uncheckAllTables: '不檢查所有表',
killQuery: '終止查詢',
insertRow: '插入單行 | 插入多行',
commitMode: '提交模式',
autoCommit: '自動提交',
manualCommit: '手動提交',
importQueryErrors: '警告: 發生了 {n} 個錯誤 | 警告: 發生了 {n} 個錯誤',
executedQueries: '{n} 個查詢已執行 | {n} 個查詢已執行',
disableFKChecks: '禁用外鍵檢查',
formatQuery: '格式查詢',
queryHistory: '查詢曆史',
clearQuery: '清除查詢',
fillCell: '填充單元格',
executeSelectedQuery: '執行所選查詢',
noResultsPresent: '沒有結果',
sqlExportOptions: 'SQL 導出選項',
targetTable: '目標表',
switchDatabase: '切換資料庫',
searchForElements: '搜索元素',
searchForSchemas: '搜索模式(schema)',
savedQueries: '已保存的查詢'
},
application: {
// 應用程式相關術語
settings: '設定',
console: '控製臺',
general: '常規',
themes: '主題',
update: '更新',
about: '關於',
language: '語言',
shortcuts: '捷徑',
key: '按鍵 | 按鍵', // 鍵盤按鍵
event: '事件',
light: '明亮',
dark: '暗黑',
autoCompletion: '自動完成',
application: '應用程式',
editor: '編輯器',
changelog: '變更日誌',
small: '小',
medium: '中',
large: '大',
appearance: '外觀',
color: '顔色',
label: '標簽',
icon: '圖示',
fileName: '檔案名稱',
choseFile: '選擇檔案',
data: '數據',
password: '密碼',
required: '依賴',
madeWithJS: '使用 💛 和 JavaScript 製作!',
checkForUpdates: '檢查更新',
noUpdatesAvailable: '無可用更新',
checkingForUpdate: '正在檢查更新',
checkFailure: '檢查失敗,請稍後再試',
updateAvailable: '可用更新',
downloadingUpdate: '正在下載更新',
updateDownloaded: '已下載更新',
restartToInstall: '重啟 Antares 以進行安裝',
includeBetaUpdates: '包含測試版更新',
notificationsTimeout: '通知超時',
openNewTab: '打開一個新標簽',
unsavedChanges: '未保存的變更',
discardUnsavedChanges: '您有一些未保存的變更, 關閉此標簽將放棄這些變更.',
applicationTheme: '應用程式主題',
editorTheme: '編輯器主題',
wrapLongLines: '將長行換行顯示',
markdownSupported: '支援 Markdown',
plantATree: '種植一棵樹',
dataTabPageSize: '資料標簽的頁面大小',
noOpenTabs: '沒有打開的標簽, 請在左側欄上導航或:',
restorePreviousSession: '恢複上一個會話',
closeTab: '關閉標簽',
goToDownloadPage: '轉到下載頁面',
disableBlur: '禁用模糊',
missingOrIncompleteTranslation: '有缺失或不完整的翻譯?',
findOutHowToContribute: '了解如何做出貢獻',
reportABug: '報告錯誤',
nextTab: '下一個標簽',
previousTab: '上一個標簽',
selectTabNumber: '選擇標簽編號 {param}',
toggleConsole: '切換控製臺',
addShortcut: '新增捷徑',
editShortcut: '編輯捷徑',
deleteShortcut: '刪除捷徑',
restoreDefaults: '恢複預設',
restoreDefaultsQuestion: '是否確認恢複預設值?',
registerAShortcut: '註冊捷徑',
invalidShortcutMessage: '無效組合,請繼續鍵入',
shortcutAlreadyExists: '捷徑已存在',
saveContent: '保存內容',
openAllConnections: '打開所有連接',
openSettings: '打開設定',
openScratchpad: '打開草稿欄',
runOrReload: '運行或重新加載',
openFilter: '打開過濾器',
nextResultsPage: '下一個結果頁',
previousResultsPage: '上一個結果頁',
editFolder: '編輯檔案夾',
folderName: '檔案夾名稱',
deleteFolder: '刪除檔案夾',
editConnectionAppearance: '編輯連接的外觀',
defaultCopyType: '預設複製類型',
showTableSize: '在側邊欄顯示表大小',
showTableSizeDescription: '僅限 MySQL/MariaDB. 啓用此選項可能會影響許多表的模式(schema)的性能.',
switchSearchMethod: '切換搜索方法',
phpArray: 'PHP 陣列',
closeAllTabs: '關閉所有標簽',
closeOtherTabs: '關閉其他標簽',
closeTabsToLeft: '關閉左側的標簽',
closeTabsToRight: '關閉右側的標簽',
csvFieldDelimiter: '字段分隔符',
csvLinesTerminator: '行終止符',
csvStringDelimiter: '字符串分隔符',
csvIncludeHeader: '包含頁眉',
csvExportOptions: 'CSV 導出選項',
exportData: '導出數據',
exportDataExplanation: '將保存的連接導出到 Antares. 係統將要求您輸入密碼以加密導出的檔案.',
importData: '導入數據',
importDataExplanation: '導入包含連接的 .antares 檔案. 您需要輸入在導出過程中定義的密碼.',
includeConnectionPasswords: '包含連接密碼',
includeFolders: '包含檔案夾',
encryptionPassword: '加密密碼',
encryptionPasswordError: '加密密碼的長度必須至少為 8 個字符.',
ignoreDuplicates: '忽略重複',
wrongImportPassword: '錯誤的導入密碼',
wrongFileFormat: '錯誤的檔案格式',
dataImportSuccess: '數據已成功導入',
note: '筆記 | 筆記',
thereAreNoNotesYet: '目前還沒有筆記',
addNote: '新增筆記',
editNote: '編輯筆記',
saveAsNote: '另存為筆記',
showArchivedNotes: '顯示歸檔筆記',
hideArchivedNotes: '隱藏歸檔筆記',
tag: '筆記標簽', // Note tag,
saveFile: '保存檔案',
saveFileAs: '將檔案另存為',
openFile: '打開檔案',
openNotes: '打開筆記'
},
faker: {
// Faker.js 方法,用於隨機生成的內容
address: '地址',
commerce: '商業',
company: '公司',
database: '資料庫',
date: '日期',
finance: '財務',
git: 'Git',
hacker: '駭客',
internet: '網際網路',
lorem: 'Lorem',
name: '姓名',
music: '音樂',
phone: '電話',
random: '隨機',
system: '系統',
time: '時間',
vehicle: '車輛',
zipCode: '郵政編碼',
zipCodeByState: '各州的郵政編碼',
city: '城市',
cityPrefix: '城市前綴',
citySuffix: '城市字尾',
streetName: '街道名稱',
streetAddress: '街道地址',
streetSuffix: '街道字尾',
streetPrefix: '街道前綴',
secondaryAddress: '次要地址',
county: '縣',
country: '國家',
countryCode: '國家代碼',
state: '州',
stateAbbr: '州簡稱',
latitude: '緯度',
longitude: '經度',
direction: '\'方向\'',
cardinalDirection: '方位',
ordinalDirection: '順序方向',
nearbyGPSCoordinate: '附近的 GPS 坐標',
timeZone: '時區',
color: '顔色',
department: '部門',
productName: '産品名稱',
price: '價格',
productAdjective: '産品形容詞',
productMaterial: '産品材料',
product: '産品',
productDescription: '産品說明',
suffixes: '字尾',
companyName: '公司名稱',
companySuffix: '公司字尾',
catchPhrase: '流行語',
bs: 'BS',
catchPhraseAdjective: '流行語形容詞',
catchPhraseDescriptor: '流行語說明',
catchPhraseNoun: '流行語名詞',
bsAdjective: 'BS 形容詞',
bsBuzz: 'BS 嗡嗡聲',
bsNoun: 'BS 名稱',
column: '列',
type: '類型',
collation: '排序規則',
engine: '引擎',
past: '過去',
now: '現在',
future: '未來',
between: '之間',
recent: '最近',
soon: '很快',
month: '月',
weekday: '工作日',
account: '帳戶',
accountName: '帳戶名稱',
routingNumber: '路由編號',
mask: '掩碼',
amount: '金額',
transactionType: '交易類型',
currencyCode: '貨幣代碼',
currencyName: '貨幣名稱',
currencySymbol: '貨幣符號',
bitcoinAddress: '位元幣地址',
litecoinAddress: '萊特幣地址',
creditCardNumber: '信用卡號碼',
creditCardCVV: '信用卡CVV',
ethereumAddress: '以太坊地址',
iban: 'Iban',
bic: 'Bic',
transactionDescription: '交易描述',
branch: '分支',
commitEntry: '提交條目',
commitMessage: '提交信息',
commitSha: '提交 SHA',
shortSha: '短的 SHA',
abbreviation: '縮寫',
adjective: '形容詞',
noun: '名詞',
verb: '動詞',
ingverb: '英式動詞',
phrase: '短語',
avatar: '頭像',
email: '電子信箱',
exampleEmail: '示例電子郵件',
userName: '使用者名',
protocol: '協議',
url: '統一資源定位地址',
domainName: '域名',
domainSuffix: '域名字尾',
domainWord: '域詞',
ip: 'Ip',
ipv6: 'Ipv6',
userAgent: '使用者代理',
mac: 'Mac',
password: '密碼',
word: '單詞',
words: '單詞',
sentence: '句子',
slug: 'Slug',
sentences: '句子',
paragraph: '段落',
paragraphs: '段落',
text: '文本',
lines: '行',
genre: '類型',
firstName: '名',
lastName: '姓氏',
middleName: '中間名',
findName: '全名',
jobTitle: '職位名稱',
gender: '性別',
prefix: '前綴',
suffix: '字尾',
title: '頭銜',
jobDescriptor: '工作描述',
jobArea: '工作領域',
jobType: '工作類型',
phoneNumber: '電話號碼',
phoneNumberFormat: '電話號碼格式',
phoneFormats: '電話格式',
number: '號碼',
float: 'Float',
arrayElement: '陣列元素',
arrayElements: '陣列元素',
objectElement: '物件元素',
uuid: 'Uuid',
boolean: '布林',
image: '鏡像',
locale: '區域',
alpha: '阿爾法',
alphaNumeric: 'α 數字',
hexaDecimal: '十六進製',
fileName: '檔案名',
commonFileName: '普通檔案名',
mimeType: 'MIME類型',
commonFileType: '常見檔案類型',
commonFileExt: '常見檔案擴展名',
fileType: '檔案類型',
fileExt: '檔案擴展名',
directoryPath: '目錄路徑',
filePath: '檔案路徑',
semver: 'Semver',
manufacturer: '製造商',
model: '型號',
fuel: 'Fuel',
vin: 'Vin'
}
};

View File

@@ -44,6 +44,15 @@ ipcRenderer.on('unhandled-exception', (event, error) => {
date: new Date()
});
});
ipcRenderer.on('non-blocking-exception', (event, error) => {
useNotificationsStore().addNotification({ status: 'error', message: error.message });
useConsoleStore().putLog('debug', {
level: 'error',
process: 'main',
message: error.message,
date: new Date()
});
});
// IPC query logs
ipcRenderer.on('query-log', (event, logRecord: QueryLog) => {

View File

@@ -15,6 +15,10 @@ export default class {
return ipcRenderer.invoke('connect', unproxify(newParams));
}
static abortConnection (uid: string): void {
ipcRenderer.send('abort-connection', uid);
}
static checkConnection (uid: string): Promise<boolean> {
return ipcRenderer.invoke('check-connection', uid);
}

View File

@@ -256,6 +256,10 @@ option:checked {
}
&.active {
a {
border-bottom-color: transparent;
}
.tab-link {
border-color: transparent;
}

View File

@@ -255,6 +255,11 @@
}
}
&.result-tabs .tab-item {
background: transparent;
}
.workspace-query-runner .workspace-query-runner-footer .workspace-query-buttons .btn {
color: $body-font-color-dark;
}

View File

@@ -147,7 +147,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
else
this.selectedWorkspace = uid;
},
async connectWorkspace (connection: ConnectionParams & { pgConnString?: string }, mode?: string) {
async connectWorkspace (connection: ConnectionParams & { pgConnString?: string }, args?: {mode?: string; signal?: AbortSignal}) {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? {
...workspace,
@@ -155,112 +155,136 @@ export const useWorkspacesStore = defineStore('workspaces', {
breadcrumbs: {},
loadedSchemas: new Set(),
database: connection.database,
connectionStatus: mode === 'switch' ? 'connected' : 'connecting'
connectionStatus: args?.mode === 'switch' ? 'connected' : 'connecting'
}
: workspace);
const connectionsStore = useConnectionsStore();
const notificationsStore = useNotificationsStore();
const settingsStore = useSettingsStore();
try {
const { status, response } = await Connection.connect(connection);
if (status === 'error') {
notificationsStore.addNotification({ status, message: response });
return new Promise((resolve, reject) => {
const abortHandler = () => {
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? {
...workspace,
structure: [],
breadcrumbs: {},
loadedSchemas: new Set(),
connectionStatus: 'failed'
connectionStatus: 'disconnected'
}
: workspace);
}
else {
let clientCustomizations: Customizations;
const { updateLastConnection } = connectionsStore;
return reject(new Error('Connection aborted by user'));
};
updateLastConnection(connection.uid);
args?.signal?.addEventListener('abort', abortHandler);
switch (connection.client) {
case 'mysql':
case 'maria':
clientCustomizations = customizations.mysql;
break;
case 'pg':
clientCustomizations = customizations.pg;
break;
case 'sqlite':
clientCustomizations = customizations.sqlite;
break;
case 'firebird':
clientCustomizations = customizations.firebird;
break;
}
const dataTypes = clientCustomizations.dataTypes;
const indexTypes = clientCustomizations.indexTypes;
(async () => {
try {
const { status, response } = await Connection.connect(connection);
const { status, response: version } = await Schema.getVersion(connection.uid);
if (status === 'error') {
notificationsStore.addNotification({ status, message: response });
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? {
...workspace,
structure: [],
breadcrumbs: {},
loadedSchemas: new Set(),
connectionStatus: 'failed'
}
: workspace);
if (status === 'error')
notificationsStore.addNotification({ status, message: version });
// Check if Maria or MySQL
const isMySQL = version.name.includes('MySQL');
const isMaria = version.name.includes('Maria');
if (isMySQL && connection.client !== 'mysql') {
const connProxy = Object.assign({}, connection);
connProxy.client = 'mysql';
connectionsStore.editConnection(connProxy);
}
else if (isMaria && connection.client === 'mysql') {
const connProxy = Object.assign({}, connection);
connProxy.client = 'maria';
connectionsStore.editConnection(connProxy);
}
const cachedTabs: WorkspaceTab[] = settingsStore.restoreTabs ? persistentStore.get(connection.uid, []) as WorkspaceTab[] : [];
if (cachedTabs.length) {
tabIndex[connection.uid] = cachedTabs.reduce((acc: number, curr) => {
if (curr.index > acc) acc = curr.index;
return acc;
}, null);
}
const selectedTab = cachedTabs.length
? connection.database
? cachedTabs.filter(tab => tab.type === 'query' || tab.database === connection.database)[0]?.uid
: cachedTabs[0].uid
: null;
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? {
...workspace,
client: connection.client,
dataTypes,
indexTypes,
customizations: clientCustomizations,
structure: response,
connectionStatus: 'connected',
tabs: cachedTabs,
selectedTab,
version
return reject(new Error(response));
}
: workspace);
else if (status === 'abort')
return reject(new Error('Connection aborted by user'));
else {
let clientCustomizations: Customizations;
const { updateLastConnection } = connectionsStore;
this.refreshCollations(connection.uid);
this.refreshVariables(connection.uid);
this.refreshEngines(connection.uid);
this.refreshUsers(connection.uid);
}
}
catch (err) {
notificationsStore.addNotification({ status: 'error', message: err.stack });
}
updateLastConnection(connection.uid);
switch (connection.client) {
case 'mysql':
case 'maria':
clientCustomizations = customizations.mysql;
break;
case 'pg':
clientCustomizations = customizations.pg;
break;
case 'sqlite':
clientCustomizations = customizations.sqlite;
break;
case 'firebird':
clientCustomizations = customizations.firebird;
break;
}
const dataTypes = clientCustomizations.dataTypes;
const indexTypes = clientCustomizations.indexTypes;
const { status, response: version } = await Schema.getVersion(connection.uid);
if (status === 'error')
notificationsStore.addNotification({ status, message: version });
// Check if Maria or MySQL
const isMySQL = version.name.includes('MySQL');
const isMaria = version.name.includes('Maria');
if (isMySQL && connection.client !== 'mysql') {
const connProxy = Object.assign({}, connection);
connProxy.client = 'mysql';
connectionsStore.editConnection(connProxy);
}
else if (isMaria && connection.client === 'mysql') {
const connProxy = Object.assign({}, connection);
connProxy.client = 'maria';
connectionsStore.editConnection(connProxy);
}
const cachedTabs: WorkspaceTab[] = settingsStore.restoreTabs ? persistentStore.get(connection.uid, []) as WorkspaceTab[] : [];
if (cachedTabs.length) {
tabIndex[connection.uid] = cachedTabs.reduce((acc: number, curr) => {
if (curr.index > acc) acc = curr.index;
return acc;
}, null);
}
const selectedTab = cachedTabs.length
? connection.database
? cachedTabs.filter(tab => tab.type === 'query' || tab.database === connection.database)[0]?.uid
: cachedTabs[0].uid
: null;
this.workspaces = (this.workspaces as Workspace[]).map(workspace => workspace.uid === connection.uid
? {
...workspace,
client: connection.client,
dataTypes,
indexTypes,
customizations: clientCustomizations,
structure: response,
connectionStatus: 'connected',
tabs: cachedTabs,
selectedTab,
version
}
: workspace);
args?.signal?.removeEventListener('abort', abortHandler);
this.refreshCollations(connection.uid);
this.refreshVariables(connection.uid);
this.refreshEngines(connection.uid);
this.refreshUsers(connection.uid);
resolve(true);
}
}
catch (err) {
notificationsStore.addNotification({ status: 'error', message: err.stack });
}
})();
});
},
async refreshStructure (uid: string) {
const notificationsStore = useNotificationsStore();
@@ -405,7 +429,7 @@ export const useWorkspacesStore = defineStore('workspaces', {
},
async switchConnection (connection: ConnectionParams & { pgConnString?: string }) {
await Connection.disconnect(connection.uid);
return this.connectWorkspace(connection, 'switch');
return this.connectWorkspace(connection, { mode: 'switch' });
},
addWorkspace (uid: string) {
const workspace: Workspace = {