Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-i18n.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ permissions:

jobs:
i18n-audit:
runs-on: blacksmith-4vcpu-ubuntu-2204
runs-on: ubuntu-22.04

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [blacksmith-4vcpu-ubuntu-2204, macos-14]
os: [ubuntu-22.04, macos-14]

steps:
- name: Checkout
Expand Down
28 changes: 23 additions & 5 deletions apps/app/src/components/chat/new-conversation-starter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export type StarterCapability = {

type TemplateCategory = "site" | "poster" | "cards" | "app" | "article" | "slides" | "report" | "other" | "video";

const TEMPLATE_CATEGORY_ICONS: Record<TemplateCategory, Icon> = {
const TEMPLATE_CATEGORY_ICONS: Record<TemplateCategory, Icon> = {
site: Globe2,
poster: Image,
cards: PanelsTopLeft,
Expand All @@ -152,8 +152,26 @@ const TEMPLATE_CATEGORY_ICONS: Record<TemplateCategory, Icon> = {
slides: Presentation,
report: FileChartColumnIncreasing,
other: FolderOpen,
video: Film,
};
video: Film,
};

const TEMPLATE_CATEGORY_LABEL_KEYS = {
site: "new_conversation.template_category.site",
poster: "new_conversation.template_category.poster",
cards: "new_conversation.template_category.cards",
app: "new_conversation.template_category.app",
article: "new_conversation.template_category.article",
slides: "new_conversation.template_category.slides",
report: "new_conversation.template_category.report",
other: "new_conversation.template_category.other",
video: "new_conversation.template_category.video",
} as const satisfies Record<TemplateCategory, string>;

const ANIMATION_UPDATE_LABEL_KEYS = {
live: "new_conversation.animations.update_live",
rebuild: "new_conversation.animations.update_rebuild",
reload: "new_conversation.animations.update_reload",
} as const;

const MODE_ACTIONS: Record<NewConversationMode, ReadonlyArray<StarterAction>> = {
work: [
Expand Down Expand Up @@ -238,7 +256,7 @@ function TemplateStrip({
const categoryTemplates = templates.filter((template) => (
template.manifest.category === category && (category !== "video" || template.manifest.surface === "video")
));
const categoryLabel = t(`new_conversation.template_category.${category}`);
const categoryLabel = t(TEMPLATE_CATEGORY_LABEL_KEYS[category]);
const CategoryIcon = TEMPLATE_CATEGORY_ICONS[category];
const scrollerRef = useRef<HTMLDivElement | null>(null);
const [scrollProgress, setScrollProgress] = useState(0);
Expand Down Expand Up @@ -665,7 +683,7 @@ function AnimationParameterDialog({
</div>
</div>
<div className="flex min-h-7 items-center justify-between rounded-lg bg-muted/60 px-2.5 text-[10px] text-muted-foreground">
<span>{lastUpdate ? t(`new_conversation.animations.update_${lastUpdate}`) : t("new_conversation.animations.preview_defaults")}</span>
<span>{lastUpdate ? t(ANIMATION_UPDATE_LABEL_KEYS[lastUpdate]) : t("new_conversation.animations.preview_defaults")}</span>
<span>{t("new_conversation.animations.current_time", { time: preservedTimeRef.current.toFixed(1) })}</span>
</div>
</div>
Expand Down
14 changes: 13 additions & 1 deletion apps/app/src/components/template-catalog-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,20 @@ export type TemplateCatalogDialogProps<Applied> = {
onApplied: (result: Applied) => void;
};

const TEMPLATE_CATEGORY_LABEL_KEYS = {
site: "template_market.category.site",
video: "template_market.category.video",
app: "template_market.category.app",
slides: "template_market.category.slides",
poster: "template_market.category.poster",
cards: "template_market.category.cards",
report: "template_market.category.report",
article: "template_market.category.article",
other: "template_market.category.other",
} as const satisfies Record<TemplateCategory, string>;

function categoryLabel(category: TemplateCategory) {
return t(`template_market.category.${category}`);
return t(TEMPLATE_CATEGORY_LABEL_KEYS[category]);
}

function TemplateCover(props: {
Expand Down
2 changes: 1 addition & 1 deletion apps/app/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ export default {
"templates.brief.reference_remove": "Remove {name}",
"templates.brief.reference_supported_formats": "Supported: PDF, DOCX, MD, TXT, PNG, JPG, JPEG, WebP, CSV, JSON.",
"templates.brief.reference_unsupported_one": "{name} is not a supported reference document.",
"templates.brief.reference_unsupported_many": "{count} files are not supported reference documents.",
"templates.brief.reference_unsupported_other": "{count} files are not supported reference documents.",
"templates.brief.reference_prepare_failed": "Could not prepare this reference document.",
"templates.brief.reference_autofilled": "Filled title, audience, and details from the reference document. You can edit them before sending.",
"templates.brief.submit_failed": "Could not submit this template brief.",
Expand Down
2 changes: 1 addition & 1 deletion apps/app/src/i18n/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ export default {
"templates.brief.reference_remove": "移除 {name}",
"templates.brief.reference_supported_formats": "支持:PDF、DOCX、MD、TXT、PNG、JPG、JPEG、WebP、CSV、JSON。",
"templates.brief.reference_unsupported_one": "{name} 不是支持的参考文档格式。",
"templates.brief.reference_unsupported_many": "{count} 个文件不是支持的参考文档格式。",
"templates.brief.reference_unsupported_other": "{count} 个文件不是支持的参考文档格式。",
"templates.brief.reference_prepare_failed": "无法准备这个参考文档。",
"templates.brief.reference_autofilled": "已根据参考文档填入标题、受众和信息,可手动修改。",
"templates.brief.submit_failed": "无法提交模板需求,请重试。",
Expand Down
4 changes: 2 additions & 2 deletions apps/app/src/react-app/domains/session/chat/session-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -817,8 +817,8 @@ function TemplateBriefCard({ template, onSubmit, onClose }: { template: Template
if (unsupported.length) {
toast.warning(
unsupported.length === 1
? t("templates.brief.reference_unsupported_one", { name: unsupported[0]?.name ?? "" })
: t("templates.brief.reference_unsupported_many", { count: unsupported.length }),
? t("templates.brief.reference_unsupported_one", { name: unsupported[0]?.name ?? "" })
: t("templates.brief.reference_unsupported_other", { count: unsupported.length }),
{ description: t("templates.brief.reference_supported_formats") },
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,27 @@ const AUTHORING_TYPES: AuthoringType[] = CATEGORIES.flatMap((category) => catego
: [{ ...category, key: category.id }]);

const STYLE_ORDER = Object.keys(TEMPLATE_STYLE_LABELS) as TemplateStyle[];
const templateStyleLabel = (style: TemplateStyle) => t(`template_market.style.${style}`);
const TEMPLATE_STYLE_LABEL_KEYS = {
minimal: "template_market.style.minimal",
editorial: "template_market.style.editorial",
newsprint: "template_market.style.newsprint",
swiss: "template_market.style.swiss",
bold: "template_market.style.bold",
soft: "template_market.style.soft",
pastel: "template_market.style.pastel",
glass: "template_market.style.glass",
dark: "template_market.style.dark",
cyber: "template_market.style.cyber",
technical: "template_market.style.technical",
playful: "template_market.style.playful",
cinematic: "template_market.style.cinematic",
data: "template_market.style.data",
brutalist: "template_market.style.brutalist",
retro: "template_market.style.retro",
sketch: "template_market.style.sketch",
custom: "template_market.style.custom",
} as const satisfies Record<TemplateStyle, string>;
const templateStyleLabel = (style: TemplateStyle) => t(TEMPLATE_STYLE_LABEL_KEYS[style]);
const TEMPLATE_COVER_TIMEOUT_MS = 12_000;
const TEMPLATE_COVER_ROOT_MARGIN = "480px 0px";

Expand Down
26 changes: 23 additions & 3 deletions apps/app/src/react-app/domains/session/video/video-voice-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,32 @@ function canSynthesizeCustomVoice(voice: CustomVoice | undefined) {
return voice?.status === "OK";
}

const PRESET_VOICE_LABEL_KEYS: Record<string, string> = {
longanyang: "video.voice.preset_name.longanyang",
longanhuan_v3: "video.voice.preset_name.longanhuan_v3",
longanlang_v3: "video.voice.preset_name.longanlang_v3",
longyingmu_v3: "video.voice.preset_name.longyingmu_v3",
};

const PRESET_VOICE_DESCRIPTION_KEYS: Record<string, string> = {
longanyang: "video.voice.preset_description.longanyang",
longanhuan_v3: "video.voice.preset_description.longanhuan_v3",
longanlang_v3: "video.voice.preset_description.longanlang_v3",
longyingmu_v3: "video.voice.preset_description.longyingmu_v3",
};

function presetVoiceLabel(voiceId: string) {
return BAILIAN_PRESET_VOICES.some((voice) => voice.id === voiceId)
? t(`video.voice.preset_name.${voiceId}`)
const labelKey = PRESET_VOICE_LABEL_KEYS[voiceId];
return BAILIAN_PRESET_VOICES.some((voice) => voice.id === voiceId) && labelKey
? t(labelKey)
: voiceId;
}

function presetVoiceDescription(voiceId: string) {
const descriptionKey = PRESET_VOICE_DESCRIPTION_KEYS[voiceId];
return descriptionKey ? t(descriptionKey) : "";
}

function customVoiceAvailabilityMessage(voice: CustomVoice | undefined) {
if (!voice) return t("video.voice.error.cloned_not_found");
if (voice.status === "DEPLOYING") return t("video.voice.error.cloned_deploying");
Expand Down Expand Up @@ -410,7 +430,7 @@ export function VideoVoicePanel({ sessionId, workspaceRoot, client, workspaceId,
</div>
<Select value={presetVoiceId} onValueChange={(value) => { if (value) void choosePreset(value); }}>
<SelectTrigger className="w-full" aria-label={t("video.voice.official_aria")}><SelectValue placeholder={t("video.voice.choose_official")} /></SelectTrigger>
<SelectContent align="start"><SelectGroup><SelectLabel>CosyVoice</SelectLabel>{BAILIAN_PRESET_VOICES.map((voice) => <SelectItem key={voice.id} value={voice.id}>{presetVoiceLabel(voice.id)} · {t(`video.voice.preset_description.${voice.id}`)}</SelectItem>)}</SelectGroup></SelectContent>
<SelectContent align="start"><SelectGroup><SelectLabel>CosyVoice</SelectLabel>{BAILIAN_PRESET_VOICES.map((voice) => <SelectItem key={voice.id} value={voice.id}>{presetVoiceLabel(voice.id)} · {presetVoiceDescription(voice.id)}</SelectItem>)}</SelectGroup></SelectContent>
</Select>
{activeVoice?.source === "preset" ? <SelectedVoice voiceId={activeVoice.voiceId} label={presetVoiceLabel(activeVoice.voiceId)} /> : null}
<VoiceAiButton disabled={!activeVoice || activeVoice.source !== "preset"} onClick={sendVoiceToAi} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ const categoryResolutionOrder: Exclude<MarketplaceCategoryId, "other">[] = [
"development-operations",
];

const MARKETPLACE_CATEGORY_LABEL_KEYS = {
"ai-agents": "plugin_library.category.ai-agents",
"development-operations": "plugin_library.category.development-operations",
"design-creative": "plugin_library.category.design-creative",
"productivity-collaboration": "plugin_library.category.productivity-collaboration",
"business-operations": "plugin_library.category.business-operations",
finance: "plugin_library.category.finance",
other: "plugin_library.category.other",
} as const satisfies Record<MarketplaceCategoryId, string>;

const MARKETPLACE_STATUS_LABEL_KEYS = {
available: "plugin_library.status_available",
installed: "plugin_library.status_installed",
update: "plugin_library.status_update",
} as const satisfies Record<Exclude<MarketplaceStatusFilter, "all">, string>;

export type CloudMarketplacesViewProps = {
client: iPolloWorkServerClient | null;
workspaceId: string | null;
Expand Down Expand Up @@ -85,8 +101,12 @@ export function resolveMarketplaceCategory(item: {
return "other";
}

function categoryLabel(categoryId: MarketplaceCategoryId): string {
return t(`plugin_library.category.${categoryId}`);
export function marketplaceCategoryLabel(categoryId: MarketplaceCategoryId): string {
return t(MARKETPLACE_CATEGORY_LABEL_KEYS[categoryId]);
}

export function marketplaceStatusLabel(status: Exclude<MarketplaceStatusFilter, "all">): string {
return t(MARKETPLACE_STATUS_LABEL_KEYS[status]);
}

function resourcePluginId(resource: EnterpriseResource): string {
Expand Down Expand Up @@ -314,7 +334,7 @@ export function CloudMarketplacesView({
{!embedded ? categorySections.map((section) => (
<MarketplaceSection
key={section.categoryId}
title={categoryLabel(section.categoryId)}
title={marketplaceCategoryLabel(section.categoryId)}
items={section.items}
installed={installed}
busyId={busyId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import { PluginPackageImportModal } from "./plugin-package-import-modal";
import { PluginPackageListItem } from "./plugin-package-list-item";
import {
MARKETPLACE_CATEGORY_IDS,
marketplaceCategoryLabel,
marketplaceStatusLabel,
type MarketplaceCategoryFilter,
type MarketplaceStatusFilter,
} from "./pages/cloud-marketplaces-view";
Expand Down Expand Up @@ -904,13 +906,13 @@ export const PluginPackagesPanel = forwardRef<PluginPackagesPanelHandle, PluginP
<SelectValue>
{marketplaceCategory === "all"
? t("plugin_library.all_categories")
: t(`plugin_library.category.${marketplaceCategory}`)}
: marketplaceCategoryLabel(marketplaceCategory)}
</SelectValue>
</SelectTrigger>
<SelectContent align="end">
<SelectItem value="all">{t("plugin_library.all_categories")}</SelectItem>
{MARKETPLACE_CATEGORY_IDS.map((categoryId) => (
<SelectItem key={categoryId} value={categoryId}>{t(`plugin_library.category.${categoryId}`)}</SelectItem>
<SelectItem key={categoryId} value={categoryId}>{marketplaceCategoryLabel(categoryId)}</SelectItem>
))}
</SelectContent>
</Select>
Expand All @@ -919,7 +921,7 @@ export const PluginPackagesPanel = forwardRef<PluginPackagesPanelHandle, PluginP
<SelectValue>
{marketplaceStatus === "all"
? t("plugin_library.all_statuses")
: t(`plugin_library.status_${marketplaceStatus}`)}
: marketplaceStatusLabel(marketplaceStatus)}
</SelectValue>
</SelectTrigger>
<SelectContent align="end">
Expand Down
2 changes: 1 addition & 1 deletion apps/app/tests/new-conversation-animation-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe("new conversation animation catalog", () => {
test("edits validated effect variables and sends structured configuration", () => {
expect(starter).toContain("function AnimationParameterDialog");
expect(starter).toContain("updateHyperframesEffectVariableOverride");
expect(starter).toContain("new_conversation.animations.update_${lastUpdate}");
expect(starter).toContain("ANIMATION_UPDATE_LABEL_KEYS[lastUpdate]");
expect(surface).toContain("hyperframesSelectionPayload(selection)");
expect(surface).toContain("data-variable-values/getVariables");
});
Expand Down