From 16dcf865ed378dd20f24fdd34e81fb899a4643de Mon Sep 17 00:00:00 2001
From: Neil <4138956+nwparker@users.noreply.github.com>
Date: Tue, 28 Jul 2026 02:22:35 -0700
Subject: [PATCH] fix(i18n): translate automation cadence labels (#11068)
The cadence picker now resolves Hourly, Daily, Weekdays, Weekly, and Custom cron through the renderer i18n catalog, with corrected zh/ko/es translations.
Supersedes #10044 (thanks @innocarpe) and #10045 (thanks @fsdwen).
Fixes #10043
---
.../AutomationSchedulePicker.test.ts | 34 +++++++++++++++++--
.../automations/AutomationSchedulePicker.tsx | 24 ++++++++-----
src/renderer/src/i18n/locales/en.json | 5 +++
src/renderer/src/i18n/locales/es.json | 7 +++-
src/renderer/src/i18n/locales/ja.json | 5 +++
src/renderer/src/i18n/locales/ko.json | 7 +++-
src/renderer/src/i18n/locales/zh.json | 7 +++-
7 files changed, 75 insertions(+), 14 deletions(-)
diff --git a/src/renderer/src/components/automations/AutomationSchedulePicker.test.ts b/src/renderer/src/components/automations/AutomationSchedulePicker.test.ts
index f5bc9c432..9d3837b19 100644
--- a/src/renderer/src/components/automations/AutomationSchedulePicker.test.ts
+++ b/src/renderer/src/components/automations/AutomationSchedulePicker.test.ts
@@ -1,6 +1,6 @@
import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
-import { describe, expect, it } from 'vitest'
+import { afterEach, describe, expect, it } from 'vitest'
import type { AutomationDraft } from './AutomationEditorDialog'
import {
AutomationCustomCronPanel,
@@ -9,9 +9,11 @@ import {
} from './AutomationCustomCronPanel'
import {
AUTOMATION_SCHEDULE_PRESET_OPTIONS,
+ getAutomationSchedulePresetLabel,
getSchedulePresetDraft
} from './AutomationSchedulePicker'
import { isValidAutomationCronSchedule } from '../../../../shared/automation-schedules'
+import { i18n } from '@/i18n/i18n'
const BASE_DRAFT: AutomationDraft = {
name: '',
@@ -33,8 +35,34 @@ const BASE_DRAFT: AutomationDraft = {
}
describe('AutomationSchedulePicker', () => {
- it('offers custom cron as a selectable cadence', () => {
- expect(AUTOMATION_SCHEDULE_PRESET_OPTIONS).toContainEqual(['custom', 'Custom cron'])
+ // Reset here, not after the assertion: a failed expect would otherwise leak the locale.
+ afterEach(async () => {
+ await i18n.changeLanguage('en')
+ })
+
+ it('provides an i18n key for every selectable cadence (#10043)', () => {
+ expect(AUTOMATION_SCHEDULE_PRESET_OPTIONS).toContainEqual([
+ 'custom',
+ 'Custom cron',
+ 'auto.components.automations.AutomationSchedulePicker.ddba78647e'
+ ])
+ for (const [value, fallbackLabel, labelKey] of AUTOMATION_SCHEDULE_PRESET_OPTIONS) {
+ expect(value).not.toBe('')
+ expect(fallbackLabel).not.toBe('')
+ expect(labelKey).toMatch(
+ /^auto\.components\.automations\.AutomationSchedulePicker\.[0-9a-f]{10}$/
+ )
+ }
+ })
+
+ it.each([
+ ['zh', ['每小时', '每天', '工作日', '每周', '自定义 cron']],
+ ['ja', ['毎時', '毎日', '平日', '毎週', 'カスタム cron']],
+ ['ko', ['매시간', '매일', '평일', '매주', '사용자 지정 cron']],
+ ['es', ['Cada hora', 'Diario', 'Días laborables', 'Semanal', 'Cron personalizado']]
+ ])('translates every cadence option in %s', async (locale, labels) => {
+ await i18n.changeLanguage(locale)
+ expect(AUTOMATION_SCHEDULE_PRESET_OPTIONS.map(getAutomationSchedulePresetLabel)).toEqual(labels)
})
it('seeds custom cron from the current simple schedule', () => {
diff --git a/src/renderer/src/components/automations/AutomationSchedulePicker.tsx b/src/renderer/src/components/automations/AutomationSchedulePicker.tsx
index eb203f9c3..1f6f6d96e 100644
--- a/src/renderer/src/components/automations/AutomationSchedulePicker.tsx
+++ b/src/renderer/src/components/automations/AutomationSchedulePicker.tsx
@@ -25,12 +25,20 @@ import { translate } from '@/i18n/i18n'
const FIELD_CONTROL_CLASS = 'border-input bg-input/30 shadow-xs dark:bg-input/30'
export const AUTOMATION_SCHEDULE_PRESET_OPTIONS = [
- ['hourly', 'Hourly'],
- ['daily', 'Daily'],
- ['weekdays', 'Weekdays'],
- ['weekly', 'Weekly'],
- ['custom', 'Custom cron']
-] as const satisfies readonly [AutomationSchedulePreset, string][]
+ ['hourly', 'Hourly', 'auto.components.automations.AutomationSchedulePicker.55b2ef82a4'],
+ ['daily', 'Daily', 'auto.components.automations.AutomationSchedulePicker.f0202f3a89'],
+ ['weekdays', 'Weekdays', 'auto.components.automations.AutomationSchedulePicker.57e83307d0'],
+ ['weekly', 'Weekly', 'auto.components.automations.AutomationSchedulePicker.837d902bba'],
+ ['custom', 'Custom cron', 'auto.components.automations.AutomationSchedulePicker.ddba78647e']
+] as const satisfies readonly (readonly [AutomationSchedulePreset, string, string])[]
+
+export function getAutomationSchedulePresetLabel([, fallbackLabel, labelKey]: readonly [
+ AutomationSchedulePreset,
+ string,
+ string
+]): string {
+ return translate(labelKey, fallbackLabel)
+}
const DAY_OPTIONS = [
['0', 'Sunday'],
@@ -191,9 +199,9 @@ export function AutomationSchedulePicker({
- {AUTOMATION_SCHEDULE_PRESET_OPTIONS.map(([value, presetLabel]) => (
+ {AUTOMATION_SCHEDULE_PRESET_OPTIONS.map(([value, fallbackLabel, labelKey]) => (
- {presetLabel}
+ {getAutomationSchedulePresetLabel([value, fallbackLabel, labelKey])}
))}
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index 252253011..1da5948ac 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -13359,6 +13359,11 @@
"d90981f766": "Time",
"6b914c5fbb": "Day",
"233b8c94b6": "Cadence",
+ "55b2ef82a4": "Hourly",
+ "f0202f3a89": "Daily",
+ "57e83307d0": "Weekdays",
+ "837d902bba": "Weekly",
+ "ddba78647e": "Custom cron",
"b08ccb4d06": "hourly",
"8cc026fd73": "weekly",
"c3e39e17cf": "custom"
diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json
index 302f24f7b..512f4ccff 100644
--- a/src/renderer/src/i18n/locales/es.json
+++ b/src/renderer/src/i18n/locales/es.json
@@ -13336,9 +13336,14 @@
"d90981f766": "Tiempo",
"6b914c5fbb": "Día",
"233b8c94b6": "Cadencia",
+ "55b2ef82a4": "Cada hora",
+ "f0202f3a89": "Diario",
+ "57e83307d0": "Días laborables",
+ "837d902bba": "Semanal",
+ "ddba78647e": "Cron personalizado",
"b08ccb4d06": "cada hora",
"8cc026fd73": "semanalmente",
- "c3e39e17cf": "costumbre"
+ "c3e39e17cf": "personalizado"
},
"AutomationSessionField": {
"f3c76dce51": "Reutilizar",
diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json
index a3b5555a8..3bcc69693 100644
--- a/src/renderer/src/i18n/locales/ja.json
+++ b/src/renderer/src/i18n/locales/ja.json
@@ -13336,6 +13336,11 @@
"d90981f766": "時間",
"6b914c5fbb": "日",
"233b8c94b6": "ケイデンス",
+ "55b2ef82a4": "毎時",
+ "f0202f3a89": "毎日",
+ "57e83307d0": "平日",
+ "837d902bba": "毎週",
+ "ddba78647e": "カスタム cron",
"b08ccb4d06": "毎時",
"8cc026fd73": "毎週",
"c3e39e17cf": "カスタム"
diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json
index 91878a71e..786a783c2 100644
--- a/src/renderer/src/i18n/locales/ko.json
+++ b/src/renderer/src/i18n/locales/ko.json
@@ -13336,9 +13336,14 @@
"d90981f766": "시간",
"6b914c5fbb": "요일",
"233b8c94b6": "반복 주기",
+ "55b2ef82a4": "매시간",
+ "f0202f3a89": "매일",
+ "57e83307d0": "평일",
+ "837d902bba": "매주",
+ "ddba78647e": "사용자 지정 cron",
"b08ccb4d06": "매시간",
"8cc026fd73": "주간",
- "c3e39e17cf": "custom"
+ "c3e39e17cf": "사용자 지정"
},
"AutomationSessionField": {
"f3c76dce51": "재사용",
diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json
index 70c2933ab..261cc7a45 100644
--- a/src/renderer/src/i18n/locales/zh.json
+++ b/src/renderer/src/i18n/locales/zh.json
@@ -13336,9 +13336,14 @@
"d90981f766": "时间",
"6b914c5fbb": "天",
"233b8c94b6": "节奏",
+ "55b2ef82a4": "每小时",
+ "f0202f3a89": "每天",
+ "57e83307d0": "工作日",
+ "837d902bba": "每周",
+ "ddba78647e": "自定义 cron",
"b08ccb4d06": "每小时",
"8cc026fd73": "每周",
- "c3e39e17cf": "风俗"
+ "c3e39e17cf": "自定义"
},
"AutomationSessionField": {
"f3c76dce51": "重复利用",