mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 15:45:23 +00:00
fix: proper plural forms for count strings and full translations
This commit is contained in:
1 parent
874ad9f4fd
commit
83968ede80
47 files changed
+70219
-39100
No files matched your search
@@ -14,6 +14,7 @@
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Regional browser languages like `es-MX`, `pt` or `zh-HK` showed English instead of the closest translation
|
- Regional browser languages like `es-MX`, `pt` or `zh-HK` showed English instead of the closest translation
|
||||||
|
- Counts like "1 keys" or "Moved 1 host(s)" now use the singular
|
||||||
|
|
||||||
## 26.10.0
|
## 26.10.0
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,30 @@ const OUT = path.join(
|
|||||||
"settings-search-index.ts",
|
"settings-search-index.ts",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function flatKeys(obj, prefix = "") {
|
||||||
|
return Object.entries(obj).flatMap(([key, value]) =>
|
||||||
|
typeof value === "object"
|
||||||
|
? flatKeys(value, `${prefix}${key}.`)
|
||||||
|
: [`${prefix}${key}`],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EN_KEYS = new Set(
|
||||||
|
flatKeys(require(path.join(ROOT, "src", "ui", "locales", "en.json"))),
|
||||||
|
);
|
||||||
|
|
||||||
|
// t("x", { count }) reads x_one or x_other, so index the plural key.
|
||||||
|
function realKey(key) {
|
||||||
|
return EN_KEYS.has(key) || !EN_KEYS.has(`${key}_other`)
|
||||||
|
? key
|
||||||
|
: `${key}_other`;
|
||||||
|
}
|
||||||
|
|
||||||
function keysIn(text) {
|
function keysIn(text) {
|
||||||
return [
|
return [
|
||||||
...new Set([...text.matchAll(/\bt\(\s*"([\w.-]+)"/g)].map((m) => m[1])),
|
...new Set(
|
||||||
|
[...text.matchAll(/\bt\(\s*"([\w.-]+)"/g)].map((m) => realKey(m[1])),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-17
@@ -344,7 +344,8 @@
|
|||||||
"unsavedChangesDescription": "Your host changes have not been saved. Discard them?",
|
"unsavedChangesDescription": "Your host changes have not been saved. Discard them?",
|
||||||
"keepEditing": "Keep editing",
|
"keepEditing": "Keep editing",
|
||||||
"discardChanges": "Discard changes",
|
"discardChanges": "Discard changes",
|
||||||
"movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"",
|
"movedToFolder_one": "Moved {{count}} host to \"{{folder}}\"",
|
||||||
|
"movedToFolder_other": "Moved {{count}} hosts to \"{{folder}}\"",
|
||||||
"editHostTooltip": "Edit host",
|
"editHostTooltip": "Edit host",
|
||||||
"behavior": "Behavior",
|
"behavior": "Behavior",
|
||||||
"scrollbackBuffer": "Scrollback Buffer",
|
"scrollbackBuffer": "Scrollback Buffer",
|
||||||
@@ -545,7 +546,8 @@
|
|||||||
"collapseSubHosts": "Collapse sub-hosts",
|
"collapseSubHosts": "Collapse sub-hosts",
|
||||||
"expandSubHosts": "Expand sub-hosts",
|
"expandSubHosts": "Expand sub-hosts",
|
||||||
"cannotNestUnderDescendant": "Can't nest a host under its own sub-host",
|
"cannotNestUnderDescendant": "Can't nest a host under its own sub-host",
|
||||||
"movedToParent": "Moved {{count}} host(s) under \"{{parent}}\"",
|
"movedToParent_one": "Moved {{count}} host under \"{{parent}}\"",
|
||||||
|
"movedToParent_other": "Moved {{count}} hosts under \"{{parent}}\"",
|
||||||
"newFolder": "New folder",
|
"newFolder": "New folder",
|
||||||
"createFolderTitle": "Create folder",
|
"createFolderTitle": "Create folder",
|
||||||
"editFolderTitle": "Edit folder",
|
"editFolderTitle": "Edit folder",
|
||||||
@@ -573,7 +575,8 @@
|
|||||||
"folderSaved": "Folder saved",
|
"folderSaved": "Folder saved",
|
||||||
"failedToSaveFolder": "Failed to save folder",
|
"failedToSaveFolder": "Failed to save folder",
|
||||||
"folderDeleted": "Deleted folder \"{{name}}\"",
|
"folderDeleted": "Deleted folder \"{{name}}\"",
|
||||||
"deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.",
|
"deleteFolderConfirm_one": "Delete \"{{name}}\" and its {{count}} host? This cannot be undone.",
|
||||||
|
"deleteFolderConfirm_other": "Delete \"{{name}}\" and its {{count}} hosts? This cannot be undone.",
|
||||||
"failedToMoveHosts": "Failed to move hosts",
|
"failedToMoveHosts": "Failed to move hosts",
|
||||||
"failedToReorder": "Failed to reorder",
|
"failedToReorder": "Failed to reorder",
|
||||||
"expandAll": "Expand all folders",
|
"expandAll": "Expand all folders",
|
||||||
@@ -641,7 +644,8 @@
|
|||||||
"copyLink": "Copy Link",
|
"copyLink": "Copy Link",
|
||||||
"deleteCredentialConfirm": "Delete credential \"{{name}}\"?",
|
"deleteCredentialConfirm": "Delete credential \"{{name}}\"?",
|
||||||
"deletedCredential": "Deleted {{name}}",
|
"deletedCredential": "Deleted {{name}}",
|
||||||
"deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.",
|
"deleteHostsConfirm_one": "Delete {{count}} host? This cannot be undone.",
|
||||||
|
"deleteHostsConfirm_other": "Delete {{count}} hosts? This cannot be undone.",
|
||||||
"movedToRoot": "Moved to root",
|
"movedToRoot": "Moved to root",
|
||||||
"addTagsPlaceholder": "Add tags...",
|
"addTagsPlaceholder": "Add tags...",
|
||||||
"authDetails": "Authentication Details",
|
"authDetails": "Authentication Details",
|
||||||
@@ -700,7 +704,8 @@
|
|||||||
"groupFeatureFlags": "Plugin settings",
|
"groupFeatureFlags": "Plugin settings",
|
||||||
"groupAdvanced": "Advanced config",
|
"groupAdvanced": "Advanced config",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"moreHosts": "... {{count}} more hosts",
|
"moreHosts_one": "... {{count}} more host",
|
||||||
|
"moreHosts_other": "... {{count}} more hosts",
|
||||||
"summary": "{{selected}} of {{total}} hosts",
|
"summary": "{{selected}} of {{total}} hosts",
|
||||||
"credentialsIncluded": "credentials included",
|
"credentialsIncluded": "credentials included",
|
||||||
"credentialsExcluded": "credentials excluded",
|
"credentialsExcluded": "credentials excluded",
|
||||||
@@ -718,11 +723,14 @@
|
|||||||
"moveMenu": "Move",
|
"moveMenu": "Move",
|
||||||
"connectSelected": "Connect",
|
"connectSelected": "Connect",
|
||||||
"cancelSelection": "Cancel",
|
"cancelSelection": "Cancel",
|
||||||
"deletedCount": "Deleted {{count}} hosts",
|
"deletedCount_one": "Deleted {{count}} host",
|
||||||
"failedToDeleteCount": "Failed to delete {{count}} hosts",
|
"deletedCount_other": "Deleted {{count}} hosts",
|
||||||
|
"failedToDeleteCount_one": "Failed to delete {{count}} host",
|
||||||
|
"failedToDeleteCount_other": "Failed to delete {{count}} hosts",
|
||||||
"duplicatedHost": "Duplicated \"{{name}}\"",
|
"duplicatedHost": "Duplicated \"{{name}}\"",
|
||||||
"failedToDuplicateHost": "Failed to duplicate host",
|
"failedToDuplicateHost": "Failed to duplicate host",
|
||||||
"updatedCount": "Updated {{count}} hosts",
|
"updatedCount_one": "Updated {{count}} host",
|
||||||
|
"updatedCount_other": "Updated {{count}} hosts",
|
||||||
"friendlyNameLabel": "Friendly Name",
|
"friendlyNameLabel": "Friendly Name",
|
||||||
"descriptionLabel": "Description",
|
"descriptionLabel": "Description",
|
||||||
"loadingHosts": "Loading hosts...",
|
"loadingHosts": "Loading hosts...",
|
||||||
@@ -762,7 +770,8 @@
|
|||||||
"shareHostTitle": "Share: {{name}}",
|
"shareHostTitle": "Share: {{name}}",
|
||||||
"shareFolder": "Share Folder",
|
"shareFolder": "Share Folder",
|
||||||
"shareFolderTitle": "Share folder: {{name}}",
|
"shareFolderTitle": "Share folder: {{name}}",
|
||||||
"folderSharedSuccessfully": "Shared {{count}} host(s) in folder",
|
"folderSharedSuccessfully_one": "Shared {{count}} host in folder",
|
||||||
|
"folderSharedSuccessfully_other": "Shared {{count}} hosts in folder",
|
||||||
"failedToShareFolder": "Failed to share folder",
|
"failedToShareFolder": "Failed to share folder",
|
||||||
"sharing": {
|
"sharing": {
|
||||||
"loadError": "Failed to load sharing data. Please try again.",
|
"loadError": "Failed to load sharing data. Please try again.",
|
||||||
@@ -921,7 +930,8 @@
|
|||||||
"unsplit": "Unsplit",
|
"unsplit": "Unsplit",
|
||||||
"layouts": "Layouts",
|
"layouts": "Layouts",
|
||||||
"applyLayout": "Change layout",
|
"applyLayout": "Change layout",
|
||||||
"maxPanes": "A split can hold up to {{count}} panes",
|
"maxPanes_one": "A split can hold up to {{count}} pane",
|
||||||
|
"maxPanes_other": "A split can hold up to {{count}} panes",
|
||||||
"dropSplit": "Drop to split",
|
"dropSplit": "Drop to split",
|
||||||
"dropHere": "Drop to show here",
|
"dropHere": "Drop to show here",
|
||||||
"paneActions": "Pane actions",
|
"paneActions": "Pane actions",
|
||||||
@@ -1237,7 +1247,8 @@
|
|||||||
"statusCheck": "Status Check",
|
"statusCheck": "Status Check",
|
||||||
"sec": "sec",
|
"sec": "sec",
|
||||||
"logLevel": "Log Level",
|
"logLevel": "Log Level",
|
||||||
"usersCount": "{{count}} users",
|
"usersCount_one": "{{count}} user",
|
||||||
|
"usersCount_other": "{{count}} users",
|
||||||
"searchUsers": "Search users...",
|
"searchUsers": "Search users...",
|
||||||
"noUsers": "No users",
|
"noUsers": "No users",
|
||||||
"noUsersMatch": "No users match your search",
|
"noUsersMatch": "No users match your search",
|
||||||
@@ -1247,7 +1258,8 @@
|
|||||||
"roleName": "Name",
|
"roleName": "Name",
|
||||||
"roleDisplayName": "Display Name",
|
"roleDisplayName": "Display Name",
|
||||||
"roleDescription": "Description",
|
"roleDescription": "Description",
|
||||||
"rolesCount": "{{count}} roles",
|
"rolesCount_one": "{{count}} role",
|
||||||
|
"rolesCount_other": "{{count}} roles",
|
||||||
"createRole": "Create",
|
"createRole": "Create",
|
||||||
"creating": "Creating...",
|
"creating": "Creating...",
|
||||||
"exportDatabase": "Export Database",
|
"exportDatabase": "Export Database",
|
||||||
@@ -1261,7 +1273,8 @@
|
|||||||
"changeFile": "Change",
|
"changeFile": "Change",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"importing": "Importing...",
|
"importing": "Importing...",
|
||||||
"apiKeysCount": "{{count}} keys",
|
"apiKeysCount_one": "{{count}} key",
|
||||||
|
"apiKeysCount_other": "{{count}} keys",
|
||||||
"apiKeysDocsLink": "View docs",
|
"apiKeysDocsLink": "View docs",
|
||||||
"apiKeysCliHint": "Use an API key with the Termix CLI to manage servers from your terminal.",
|
"apiKeysCliHint": "Use an API key with the Termix CLI to manage servers from your terminal.",
|
||||||
"apiKeysCliLink": "View CLI docs",
|
"apiKeysCliLink": "View CLI docs",
|
||||||
@@ -1392,14 +1405,16 @@
|
|||||||
"exportUserDataDesc": "Download this user's hosts, credentials and file manager data as JSON. Secrets are decrypted.",
|
"exportUserDataDesc": "Download this user's hosts, credentials and file manager data as JSON. Secrets are decrypted.",
|
||||||
"exportUserDataSuccess": "User data exported",
|
"exportUserDataSuccess": "User data exported",
|
||||||
"exportUserDataFailed": "Failed to export user data",
|
"exportUserDataFailed": "Failed to export user data",
|
||||||
"hostsCount": "{{count}} hosts",
|
"hostsCount_one": "{{count}} host",
|
||||||
|
"hostsCount_other": "{{count}} hosts",
|
||||||
"addHostForUser": "Add Host",
|
"addHostForUser": "Add Host",
|
||||||
"noHostsForUser": "This user has no hosts",
|
"noHostsForUser": "This user has no hosts",
|
||||||
"connectToHost": "Connect",
|
"connectToHost": "Connect",
|
||||||
"deleteHostConfirm": "Delete host \"{{name}}\" belonging to {{username}}?",
|
"deleteHostConfirm": "Delete host \"{{name}}\" belonging to {{username}}?",
|
||||||
"hostDeletedSuccess": "Host deleted",
|
"hostDeletedSuccess": "Host deleted",
|
||||||
"hostDeleteFailed": "Failed to delete host",
|
"hostDeleteFailed": "Failed to delete host",
|
||||||
"credentialsCount": "{{count}} credentials",
|
"credentialsCount_one": "{{count}} credential",
|
||||||
|
"credentialsCount_other": "{{count}} credentials",
|
||||||
"addCredentialForUser": "Add Credential",
|
"addCredentialForUser": "Add Credential",
|
||||||
"noCredentialsForUser": "This user has no credentials",
|
"noCredentialsForUser": "This user has no credentials",
|
||||||
"deleteCredentialConfirm": "Delete credential \"{{name}}\" belonging to {{username}}?",
|
"deleteCredentialConfirm": "Delete credential \"{{name}}\" belonging to {{username}}?",
|
||||||
@@ -1428,7 +1443,8 @@
|
|||||||
"importError": "Database import failed",
|
"importError": "Database import failed",
|
||||||
"sectionHostDefaults": "Host Defaults",
|
"sectionHostDefaults": "Host Defaults",
|
||||||
"rolePermissions": {
|
"rolePermissions": {
|
||||||
"count": "{{count}} permissions",
|
"count_one": "{{count}} permission",
|
||||||
|
"count_other": "{{count}} permissions",
|
||||||
"editAction": "Edit permissions",
|
"editAction": "Edit permissions",
|
||||||
"loadError": "Failed to load the permissions catalog",
|
"loadError": "Failed to load the permissions catalog",
|
||||||
"saved": "Role permissions saved",
|
"saved": "Role permissions saved",
|
||||||
@@ -1647,7 +1663,8 @@
|
|||||||
"apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.",
|
"apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.",
|
||||||
"copyApiKey": "Copy API key",
|
"copyApiKey": "Copy API key",
|
||||||
"copiedToClipboard": "Copied to clipboard",
|
"copiedToClipboard": "Copied to clipboard",
|
||||||
"apiKeyCount": "{{count}} keys",
|
"apiKeyCount_one": "{{count}} key",
|
||||||
|
"apiKeyCount_other": "{{count}} keys",
|
||||||
"newKey": "New Key",
|
"newKey": "New Key",
|
||||||
"noApiKeys": "No API keys yet.",
|
"noApiKeys": "No API keys yet.",
|
||||||
"apiKeyActive": "Active",
|
"apiKeyActive": "Active",
|
||||||
|
|||||||
+1517
-1196
File diff suppressed because it is too large.
Load diff
+1635
-1102
File diff suppressed because it is too large.
Load diff
+1597
-1276
File diff suppressed because it is too large.
Load diff
+1685
-1364
File diff suppressed because it is too large.
Load diff
+1548
-1174
File diff suppressed because it is too large.
Load diff
+1605
-1178
File diff suppressed because it is too large.
Load diff
+1379
-1058
File diff suppressed because it is too large.
Load diff
+1329
-1008
File diff suppressed because it is too large.
Load diff
+1726
-1405
File diff suppressed because it is too large.
Load diff
+1323
-949
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1555
-1234
File diff suppressed because it is too large.
Load diff
+1293
-919
File diff suppressed because it is too large.
Load diff
+1793
-1419
File diff suppressed because it is too large.
Load diff
+1518
-1197
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1625
-1304
File diff suppressed because it is too large.
Load diff
+1422
-1155
File diff suppressed because it is too large.
Load diff
+1355
-981
File diff suppressed because it is too large.
Load diff
+1408
-1140
File diff suppressed because it is too large.
Load diff
+1382
-1114
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1547
-1226
File diff suppressed because it is too large.
Load diff
+1404
-1083
File diff suppressed because it is too large.
Load diff
+1400
-973
File diff suppressed because it is too large.
Load diff
+1546
-1172
File diff suppressed because it is too large.
Load diff
+1439
-1065
File diff suppressed because it is too large.
Load diff
+1788
-1414
File diff suppressed because it is too large.
Load diff
+1532
-1105
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1587
-1213
File diff suppressed because it is too large.
Load diff
+1392
-1071
File diff suppressed because it is too large.
Load diff
+1453
-1185
File diff suppressed because it is too large.
Load diff
+1411
-1090
File diff suppressed because it is too large.
Load diff
+1585
-1158
File diff suppressed because it is too large.
Load diff
+1309
-1043
File diff suppressed because it is too large.
Load diff
+1239
-971
File diff suppressed because it is too large.
Load diff
+1402
-1134
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -39,7 +39,7 @@ export const SETTINGS_SEARCH_INDEX: Record<string, string[]> = {
|
|||||||
"admin.apiKeyUser",
|
"admin.apiKeyUser",
|
||||||
"admin.apiKeysCliHint",
|
"admin.apiKeysCliHint",
|
||||||
"admin.apiKeysCliLink",
|
"admin.apiKeysCliLink",
|
||||||
"admin.apiKeysCount",
|
"admin.apiKeysCount_other",
|
||||||
"admin.apiKeysDocsLink",
|
"admin.apiKeysDocsLink",
|
||||||
"admin.copiedToClipboard",
|
"admin.copiedToClipboard",
|
||||||
"admin.createKey",
|
"admin.createKey",
|
||||||
@@ -162,7 +162,7 @@ export const SETTINGS_SEARCH_INDEX: Record<string, string[]> = {
|
|||||||
"admin.roleDescription",
|
"admin.roleDescription",
|
||||||
"admin.roleDisplayName",
|
"admin.roleDisplayName",
|
||||||
"admin.roleName",
|
"admin.roleName",
|
||||||
"admin.rolePermissions.count",
|
"admin.rolePermissions.count_other",
|
||||||
"admin.rolePermissions.editAction",
|
"admin.rolePermissions.editAction",
|
||||||
"admin.rolePermissions.loadError",
|
"admin.rolePermissions.loadError",
|
||||||
"admin.rolePermissions.pluginDisabled",
|
"admin.rolePermissions.pluginDisabled",
|
||||||
@@ -170,7 +170,7 @@ export const SETTINGS_SEARCH_INDEX: Record<string, string[]> = {
|
|||||||
"admin.rolePermissions.saveError",
|
"admin.rolePermissions.saveError",
|
||||||
"admin.rolePermissions.saved",
|
"admin.rolePermissions.saved",
|
||||||
"admin.rolePermissions.saving",
|
"admin.rolePermissions.saving",
|
||||||
"admin.rolesCount",
|
"admin.rolesCount_other",
|
||||||
"admin.sectionRoles",
|
"admin.sectionRoles",
|
||||||
"admin.systemBadge",
|
"admin.systemBadge",
|
||||||
"common.cancel",
|
"common.cancel",
|
||||||
@@ -282,7 +282,7 @@ export const SETTINGS_SEARCH_INDEX: Record<string, string[]> = {
|
|||||||
"admin.userAdminAccess",
|
"admin.userAdminAccess",
|
||||||
"admin.userAdminAccessDesc",
|
"admin.userAdminAccessDesc",
|
||||||
"admin.userRoles",
|
"admin.userRoles",
|
||||||
"admin.usersCount",
|
"admin.usersCount_other",
|
||||||
"common.cancel",
|
"common.cancel",
|
||||||
"common.loading",
|
"common.loading",
|
||||||
"common.next",
|
"common.next",
|
||||||
@@ -294,7 +294,7 @@ export const SETTINGS_SEARCH_INDEX: Record<string, string[]> = {
|
|||||||
"admin.revokeKey",
|
"admin.revokeKey",
|
||||||
"admin.revokeKeyConfirm",
|
"admin.revokeKeyConfirm",
|
||||||
"newUi.sidebar.userProfile.apiKeyActive",
|
"newUi.sidebar.userProfile.apiKeyActive",
|
||||||
"newUi.sidebar.userProfile.apiKeyCount",
|
"newUi.sidebar.userProfile.apiKeyCount_other",
|
||||||
"newUi.sidebar.userProfile.apiKeyCreateFailed",
|
"newUi.sidebar.userProfile.apiKeyCreateFailed",
|
||||||
"newUi.sidebar.userProfile.apiKeyCreated",
|
"newUi.sidebar.userProfile.apiKeyCreated",
|
||||||
"newUi.sidebar.userProfile.apiKeyCreatedWarning",
|
"newUi.sidebar.userProfile.apiKeyCreatedWarning",
|
||||||
|
|||||||
@@ -1413,7 +1413,6 @@ export function SidebarTree({
|
|||||||
setConfirmDialog({
|
setConfirmDialog({
|
||||||
message: t("hosts.deleteHostsConfirm", {
|
message: t("hosts.deleteHostsConfirm", {
|
||||||
count: selectedHostIds.size,
|
count: selectedHostIds.size,
|
||||||
plural: selectedHostIds.size !== 1 ? "s" : "",
|
|
||||||
}),
|
}),
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
const ids = Array.from(selectedHostIds);
|
const ids = Array.from(selectedHostIds);
|
||||||
|
|||||||
Reference in new issue
Block a user