diff --git a/CHANGELOG.md b/CHANGELOG.md index 93aed9486..b13166d79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Create Database dialog showing MySQL charset/collation options for all database types; now shows database-specific options (encoding/LC_COLLATE for PostgreSQL, hidden for Redis/etcd) - SSH Tunnel not working with `~/.ssh/config` profiles (#672): added `Include` directive support, SSH token expansion (`%d`, `%h`, `%u`, `%r`), multi-word `Host` filtering, and detailed handshake error messages ## [0.30.1] - 2026-04-10 diff --git a/Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift b/Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift index 8826fb8e0..8a2e3d53b 100644 --- a/Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift +++ b/Plugins/MySQLDriverPlugin/MySQLPluginDriver.swift @@ -560,7 +560,9 @@ final class MySQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable { func createDatabase(name: String, charset: String, collation: String?) async throws { let escapedName = name.replacingOccurrences(of: "`", with: "``") - let validCharsets = ["utf8mb4", "utf8", "latin1", "ascii"] + let validCharsets = ["utf8mb4", "utf8mb3", "utf8", "latin1", "ascii", + "binary", "utf16", "utf32", "cp1251", "big5", + "euckr", "gb2312", "gbk", "sjis"] guard validCharsets.contains(charset) else { throw MariaDBPluginError(code: 0, message: "Invalid character set: \(charset)", sqlState: nil) } diff --git a/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift b/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift index fc29e40b7..264f459ab 100644 --- a/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift +++ b/Plugins/PostgreSQLDriverPlugin/PostgreSQLPluginDriver.swift @@ -765,7 +765,8 @@ final class PostgreSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable { func createDatabase(name: String, charset: String, collation: String?) async throws { let escapedName = name.replacingOccurrences(of: "\"", with: "\"\"") - let validCharsets = ["UTF8", "LATIN1", "SQL_ASCII"] + let validCharsets = ["UTF8", "LATIN1", "SQL_ASCII", "WIN1252", "EUC_JP", + "EUC_KR", "ISO_8859_5", "KOI8R", "SJIS", "BIG5", "GBK"] let normalizedCharset = charset.uppercased() guard validCharsets.contains(normalizedCharset) else { throw LibPQPluginError(message: "Invalid encoding: \(charset)", sqlState: nil, detail: nil) diff --git a/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift b/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift index ff23d6188..41f79f74b 100644 --- a/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift +++ b/Plugins/PostgreSQLDriverPlugin/RedshiftPluginDriver.swift @@ -621,7 +621,8 @@ final class RedshiftPluginDriver: PluginDatabaseDriver, @unchecked Sendable { func createDatabase(name: String, charset: String, collation: String?) async throws { let escapedName = name.replacingOccurrences(of: "\"", with: "\"\"") - let validCharsets = ["UTF8", "LATIN1", "SQL_ASCII"] + let validCharsets = ["UTF8", "LATIN1", "SQL_ASCII", "WIN1252", "EUC_JP", + "EUC_KR", "ISO_8859_5", "KOI8R", "SJIS", "BIG5", "GBK"] let normalizedCharset = charset.uppercased() guard validCharsets.contains(normalizedCharset) else { throw LibPQPluginError(message: "Invalid encoding: \(charset)", sqlState: nil, detail: nil) diff --git a/TablePro/Models/Schema/CreateDatabaseOptions.swift b/TablePro/Models/Schema/CreateDatabaseOptions.swift new file mode 100644 index 000000000..1d9ff1f27 --- /dev/null +++ b/TablePro/Models/Schema/CreateDatabaseOptions.swift @@ -0,0 +1,70 @@ +// +// CreateDatabaseOptions.swift +// TablePro +// +// Database-type-specific options for CREATE DATABASE dialog. +// + +import Foundation + +struct CreateDatabaseOptions { + struct Config { + let charsetLabel: String + let collationLabel: String + let defaultCharset: String + let defaultCollation: String + let charsets: [String] + let collations: [String: [String]] + let showOptions: Bool + } + + static func config(for type: DatabaseType) -> Config { + if type == .mysql || type == .mariadb { + return Config( + charsetLabel: "Character Set", + collationLabel: "Collation", + defaultCharset: "utf8mb4", + defaultCollation: "utf8mb4_unicode_ci", + charsets: CreateTableOptions.charsets, + collations: CreateTableOptions.collations, + showOptions: true + ) + } else if type == .postgresql || type == .redshift { + return Config( + charsetLabel: "Encoding", + collationLabel: "LC_COLLATE", + defaultCharset: "UTF8", + defaultCollation: "en_US.UTF-8", + charsets: postgresqlEncodings, + collations: postgresqlLocales, + showOptions: true + ) + } else { + return Config( + charsetLabel: "", + collationLabel: "", + defaultCharset: "", + defaultCollation: "", + charsets: [], + collations: [:], + showOptions: false + ) + } + } + + private static let postgresqlEncodings = [ + "UTF8", "LATIN1", "SQL_ASCII", "WIN1252", "EUC_JP", + "EUC_KR", "ISO_8859_5", "KOI8R", "SJIS", "BIG5", "GBK" + ] + + // PostgreSQL LC_COLLATE is OS-locale based, not encoding-dependent + private static let localeOptions = ["en_US.UTF-8", "C", "POSIX", "C.UTF-8"] + + private static let postgresqlLocales: [String: [String]] = { + var result: [String: [String]] = [:] + for enc in postgresqlEncodings { + result[enc] = localeOptions + } + return result + }() +} diff --git a/TablePro/Views/DatabaseSwitcher/CreateDatabaseSheet.swift b/TablePro/Views/DatabaseSwitcher/CreateDatabaseSheet.swift index cdd4c14ae..7c79ad9fa 100644 --- a/TablePro/Views/DatabaseSwitcher/CreateDatabaseSheet.swift +++ b/TablePro/Views/DatabaseSwitcher/CreateDatabaseSheet.swift @@ -10,27 +10,25 @@ import SwiftUI struct CreateDatabaseSheet: View { @Environment(\.dismiss) private var dismiss + let databaseType: DatabaseType let onCreate: (String, String, String?) async throws -> Void @State private var databaseName = "" - @State private var charset = "utf8mb4" - @State private var collation = "utf8mb4_unicode_ci" + @State private var charset: String + @State private var collation: String @State private var isCreating = false @State private var errorMessage: String? - private let charsets = [ - "utf8mb4", - "utf8", - "latin1", - "ascii" - ] + private let config: CreateDatabaseOptions.Config - private let collations: [String: [String]] = [ - "utf8mb4": ["utf8mb4_unicode_ci", "utf8mb4_general_ci", "utf8mb4_bin"], - "utf8": ["utf8_unicode_ci", "utf8_general_ci", "utf8_bin"], - "latin1": ["latin1_swedish_ci", "latin1_general_ci", "latin1_bin"], - "ascii": ["ascii_general_ci", "ascii_bin"] - ] + init(databaseType: DatabaseType, onCreate: @escaping (String, String, String?) async throws -> Void) { + self.databaseType = databaseType + self.onCreate = onCreate + let cfg = CreateDatabaseOptions.config(for: databaseType) + self.config = cfg + self._charset = State(initialValue: cfg.defaultCharset) + self._collation = State(initialValue: cfg.defaultCollation) + } var body: some View { VStack(spacing: 0) { @@ -54,36 +52,38 @@ struct CreateDatabaseSheet: View { .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) } - // Charset - VStack(alignment: .leading, spacing: 6) { - Text("Character Set") - .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .medium)) - .foregroundStyle(.secondary) - - Picker("", selection: $charset) { - ForEach(charsets, id: \.self) { cs in - Text(cs).tag(cs) + if config.showOptions { + // Charset / Encoding + VStack(alignment: .leading, spacing: 6) { + Text(config.charsetLabel) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .medium)) + .foregroundStyle(.secondary) + + Picker("", selection: $charset) { + ForEach(config.charsets, id: \.self) { cs in + Text(cs).tag(cs) + } } + .labelsHidden() + .pickerStyle(.menu) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) } - .labelsHidden() - .pickerStyle(.menu) - .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) - } - // Collation - VStack(alignment: .leading, spacing: 6) { - Text("Collation") - .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .medium)) - .foregroundStyle(.secondary) + // Collation / LC_COLLATE + VStack(alignment: .leading, spacing: 6) { + Text(config.collationLabel) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .medium)) + .foregroundStyle(.secondary) - Picker("", selection: $collation) { - ForEach(collations[charset] ?? [], id: \.self) { col in - Text(col).tag(col) + Picker("", selection: $collation) { + ForEach(config.collations[charset] ?? [], id: \.self) { col in + Text(col).tag(col) + } } + .labelsHidden() + .pickerStyle(.menu) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) } - .labelsHidden() - .pickerStyle(.menu) - .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) } // Error message @@ -116,14 +116,12 @@ struct CreateDatabaseSheet: View { } .frame(width: 380) .onExitCommand { - // Prevent dismissing the sheet via ESC while a database is being created if !isCreating { dismiss() } } .onChange(of: charset) { _, newCharset in - // Update collation when charset changes - if let firstCollation = collations[newCharset]?.first { + if let firstCollation = config.collations[newCharset]?.first { collation = firstCollation } } @@ -137,7 +135,11 @@ struct CreateDatabaseSheet: View { Task { do { - try await onCreate(databaseName, charset, collation) + if config.showOptions { + try await onCreate(databaseName, charset, collation) + } else { + try await onCreate(databaseName, "", nil) + } await MainActor.run { dismiss() } diff --git a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift index 89f4b15d3..1624e66a3 100644 --- a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift +++ b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift @@ -116,7 +116,7 @@ struct DatabaseSwitcherSheet: View { .defaultFocus($focus, .search) .task { await viewModel.fetchDatabases() } .sheet(isPresented: $showCreateDialog) { - CreateDatabaseSheet { name, charset, collation in + CreateDatabaseSheet(databaseType: databaseType) { name, charset, collation in try await viewModel.createDatabase( name: name, charset: charset, collation: collation) await viewModel.refreshDatabases() @@ -174,7 +174,9 @@ struct DatabaseSwitcherSheet: View { .help(String(localized: "Refresh database list")) // Create (only for non-SQLite) - if databaseType != .sqlite && !isSchemaMode { + if databaseType != .sqlite && databaseType != .redis + && databaseType != .etcd && !isSchemaMode + { Button(action: { showCreateDialog = true }) { Image(systemName: "plus") .frame(width: 24, height: 24) diff --git a/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift b/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift index 7fac271bf..a8ee1028a 100644 --- a/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift +++ b/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift @@ -431,7 +431,8 @@ extension MainContentCoordinator { } AppSettingsStorage.shared.saveLastDatabase(database, for: connectionId) await loadSchema() - reloadSidebar() + await schemaProvider.invalidateTables() + sidebarViewModel?.forceLoadTables() } catch { // Restore toolbar to previous database on failure toolbarState.databaseName = previousDatabase diff --git a/TablePro/Views/Main/MainContentCommandActions.swift b/TablePro/Views/Main/MainContentCommandActions.swift index 8c6f179a9..11cef1bb8 100644 --- a/TablePro/Views/Main/MainContentCommandActions.swift +++ b/TablePro/Views/Main/MainContentCommandActions.swift @@ -711,7 +711,11 @@ final class MainContentCommandActions { if let driver = DatabaseManager.shared.driver(for: self.connection.id) { coordinator?.toolbarState.databaseVersion = driver.serverVersion } - coordinator?.reloadSidebar() + // Skip sidebar reload during database switch — switchDatabase() handles it + // after schema invalidation to avoid flashing stale tables. + if coordinator?.isSwitchingDatabase != true { + coordinator?.reloadSidebar() + } coordinator?.initRedisKeyTreeIfNeeded() } } diff --git a/TablePro/Views/Sidebar/SidebarView.swift b/TablePro/Views/Sidebar/SidebarView.swift index 52cacce88..d3eecdab6 100644 --- a/TablePro/Views/Sidebar/SidebarView.swift +++ b/TablePro/Views/Sidebar/SidebarView.swift @@ -117,7 +117,9 @@ struct SidebarView: View { } .onChange(of: tables) { _, newTables in let hasSession = DatabaseManager.shared.activeSessions[connectionId] != nil - if newTables.isEmpty && hasSession && !viewModel.isLoading { + if newTables.isEmpty && hasSession && !viewModel.isLoading + && coordinator?.isSwitchingDatabase != true + { viewModel.loadTables() } } @@ -159,7 +161,7 @@ struct SidebarView: View { } private var hasActiveConnection: Bool { - viewModel.isLoading || DatabaseManager.shared.driver(for: connectionId) != nil + viewModel.isLoading } private var loadingState: some View { diff --git a/TableProMobile/TableProMobile/Localizable.xcstrings b/TableProMobile/TableProMobile/Localizable.xcstrings index bbeaaffdd..4e70d0dd2 100644 --- a/TableProMobile/TableProMobile/Localizable.xcstrings +++ b/TableProMobile/TableProMobile/Localizable.xcstrings @@ -1,6 +1,9 @@ { "sourceLanguage" : "en", "strings" : { + "" : { + + }, "%@" : { "localizations" : { "tr" : { @@ -311,6 +314,50 @@ } } }, + "All data in \"%@\" will be permanently deleted." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"%@\" içindeki tüm veriler kalıcı olarak silinecektir." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toàn bộ dữ liệu trong \"%@\" sẽ bị xóa vĩnh viễn." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"%@\"中的所有数据将被永久删除。" + } + } + } + }, + "All filter conditions will be removed." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tüm filtre koşulları kaldırılacaktır." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tất cả điều kiện lọc sẽ bị xóa." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "所有筛选条件将被移除。" + } + } + } + }, "An unknown error occurred." : { "extractionState" : "stale", "localizations" : { @@ -378,6 +425,28 @@ } } }, + "Are you sure you want to delete this connection? Saved credentials will be permanently removed." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu bağlantıyı silmek istediğinizden emin misiniz? Kayıtlı kimlik bilgileri kalıcı olarak kaldırılacaktır." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bạn có chắc muốn xóa kết nối này? Thông tin đăng nhập đã lưu sẽ bị xóa vĩnh viễn." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "确定要删除此连接吗?已保存的凭据将被永久移除。" + } + } + } + }, "Are you sure you want to delete this row? This action cannot be undone." : { "localizations" : { "tr" : { @@ -819,6 +888,28 @@ } } }, + "Clear Query" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorguyu Temizle" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa truy vấn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "清除查询" + } + } + } + }, "Color" : { "localizations" : { "tr" : { @@ -1040,6 +1131,28 @@ } } }, + "Connections in this group will be moved to ungrouped." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu gruptaki bağlantılar grupsuz bölümüne taşınacaktır." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Các kết nối trong nhóm này sẽ được chuyển sang mục chưa phân nhóm." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "此分组中的连接将被移至未分组。" + } + } + } + }, "Continue" : { "localizations" : { "tr" : { @@ -1084,6 +1197,28 @@ } } }, + "Copy Name" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adı Kopyala" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sao chép tên" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拷贝名称" + } + } + } + }, "Copy Results" : { "localizations" : { "tr" : { @@ -1128,6 +1263,28 @@ } } }, + "Copy to Clipboard" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Panoya Kopyala" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sao chép vào bộ nhớ tạm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拷贝到剪贴板" + } + } + } + }, "Copy Value" : { "localizations" : { "tr" : { @@ -1414,6 +1571,50 @@ } } }, + "Delete Connection" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bağlantıyı Sil" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除连接" + } + } + } + }, + "Delete Group" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grubu Sil" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa nhóm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除分组" + } + } + } + }, "Delete Row" : { "localizations" : { "tr" : { @@ -1480,6 +1681,50 @@ } } }, + "Drop" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaldır" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa bảng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除" + } + } + } + }, + "Drop Table" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tabloyu Kaldır" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa bảng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除表" + } + } + } + }, "Duplicate" : { "localizations" : { "tr" : { @@ -1766,6 +2011,28 @@ } } }, + "Export" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dışa Aktar" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xuất" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导出" + } + } + } + }, "Failed to load more rows" : { "extractionState" : "stale", "localizations" : { @@ -1878,6 +2145,28 @@ } } }, + "Filter" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtre" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bộ lọc" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "筛选" + } + } + } + }, "Filter by Tag" : { "localizations" : { "tr" : { @@ -3446,6 +3735,28 @@ } } }, + "Query text and results will be cleared." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sorgu metni ve sonuçları temizlenecektir." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nội dung truy vấn và kết quả sẽ bị xóa." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "查询文本和结果将被清除。" + } + } + } + }, "read-only" : { "localizations" : { "tr" : { @@ -3468,6 +3779,28 @@ } } }, + "Reconnecting..." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yeniden bağlanıyor..." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đang kết nối lại..." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "正在重新连接..." + } + } + } + }, "Reload" : { "localizations" : { "tr" : { @@ -3688,6 +4021,28 @@ } } }, + "Search all columns" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tüm sütunlarda ara" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tìm kiếm tất cả cột" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "搜索所有列" + } + } + } + }, "Search tables" : { "localizations" : { "tr" : { @@ -3865,6 +4220,72 @@ } } }, + "Share" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paylaş" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chia sẻ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "共享" + } + } + } + }, + "Share Results" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sonuçları Paylaş" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chia sẻ kết quả" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "共享结果" + } + } + } + }, + "Share Row" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Satırı Paylaş" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chia sẻ dòng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "共享行" + } + } + } + }, "Skip" : { "localizations" : { "tr" : { @@ -3909,6 +4330,28 @@ } } }, + "Sort" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sırala" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sắp xếp" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "排序" + } + } + } + }, "Sort By" : { "localizations" : { "tr" : { @@ -4196,6 +4639,28 @@ } } }, + "Table Structure" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tablo Yapısı" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cấu trúc bảng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "表结构" + } + } + } + }, "Tables" : { "localizations" : { "tr" : { @@ -4394,6 +4859,28 @@ } } }, + "The table \"%@\" and all its data will be permanently deleted." : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"%@\" tablosu ve tüm verileri kalıcı olarak silinecektir." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bảng \"%@\" và toàn bộ dữ liệu của nó sẽ bị xóa vĩnh viễn." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "表\"%@\"及其所有数据将被永久删除。" + } + } + } + }, "The table or column does not exist." : { "localizations" : { "tr" : { @@ -4571,6 +5058,50 @@ } } }, + "Truncate" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tabloyu Boşalt" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa dữ liệu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "清空" + } + } + } + }, + "Truncate Table" : { + "localizations" : { + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tabloyu Boşalt" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa dữ liệu bảng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "清空表" + } + } + } + }, "Ungrouped" : { "localizations" : { "tr" : {