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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ summarize "https://example.com" --cli codex

The default `auto` model chooses among configured providers. The [five-minute quickstart](docs/quickstart.md) covers API keys, local models, files, YouTube, podcasts, and JSON output.

### Turkish

Select Turkish summaries with either spelling:

```bash
summarize "https://example.com" --language tr
summarize "https://example.com" --language turkish
```

For Turkish CLI help and progress text, use `--locale tr` or set
`SUMMARIZE_LOCALE=tr`. English remains the default interface language.

## What it handles

| Input | Processing path |
Expand Down
4 changes: 4 additions & 0 deletions apps/chrome-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Browser extension for Chrome and Firefox that streams AI-powered summaries direc

Docs + setup: `https://summarize.sh`

The output language setting accepts `tr`/`Turkish` for Turkish summaries. In
Options → UI, set Interface language to Automatic (browser), English, or
Turkish for the extension interface.

## Build

- From repo root: `pnpm install`
Expand Down
34 changes: 26 additions & 8 deletions apps/chrome-extension/src/entrypoints/automation.content.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { defineContentScript } from "wxt/utils/define-content-script";
import { ALWAYS_ON_CONTENT_SCRIPT_EXCLUDE_MATCHES } from "../lib/content-script-matches";
import { resolveExtensionLocale, translateExtensionText } from "../lib/i18n";
import { loadSettings } from "../lib/settings";

export type ElementInfo = {
selector: string;
Expand Down Expand Up @@ -89,7 +91,10 @@ function getElementInfo(element: Element): ElementInfo {
};
}

async function createElementPicker(message?: string): Promise<ElementInfo> {
async function createElementPicker(
message: string | undefined,
locale: "en" | "tr",
): Promise<ElementInfo> {
if (window.__summarizeElementPicker) {
throw new Error("Element picker already active");
}
Expand Down Expand Up @@ -136,11 +141,14 @@ async function createElementPicker(message?: string): Promise<ElementInfo> {
`;

const bannerText = document.createElement("span");
bannerText.textContent = message || "Click an element to select • ↑↓ to change depth";
bannerText.textContent = translateExtensionText(
message || "Click an element to select • ↑↓ to change depth",
locale,
);
banner.appendChild(bannerText);

const cancelButton = document.createElement("button");
cancelButton.textContent = "Cancel (Esc)";
cancelButton.textContent = translateExtensionText("Cancel (Esc)", locale);
cancelButton.style.cssText = `
background: #1f2937;
border: none;
Expand Down Expand Up @@ -230,7 +238,7 @@ async function createElementPicker(message?: string): Promise<ElementInfo> {
});
}

function showReplOverlay(message?: string) {
function showReplOverlay(message: string | undefined, locale: "en" | "tr") {
if (window.__summarizeReplOverlay) return;
window.__summarizeReplOverlay = true;

Expand Down Expand Up @@ -287,11 +295,14 @@ function showReplOverlay(message?: string) {
card.appendChild(spinner);

const text = document.createElement("span");
text.textContent = message ? `Running: ${message}` : "Running automation…";
text.textContent = translateExtensionText(
message ? `Running: ${message}` : "Running automation…",
locale,
);
card.appendChild(text);

const abortBtn = document.createElement("button");
abortBtn.textContent = "Abort (Esc)";
abortBtn.textContent = translateExtensionText("Abort (Esc)", locale);
abortBtn.style.cssText = `
background: #1f2937;
border: none;
Expand Down Expand Up @@ -376,6 +387,13 @@ export default defineContentScript({
if ((globalThis as unknown as Record<string, unknown>)[flag]) return;
(globalThis as unknown as Record<string, unknown>)[flag] = true;

let locale = resolveExtensionLocale();
void loadSettings()
.then((settings) => {
locale = resolveExtensionLocale(settings.uiLocale);
})
.catch(() => undefined);

handleNativeInputBridge();

chrome.runtime.onMessage.addListener(
Expand All @@ -387,7 +405,7 @@ export default defineContentScript({
if (raw?.type === "automation:pick-element") {
void (async () => {
try {
const result = await createElementPicker(raw.message ?? undefined);
const result = await createElementPicker(raw.message ?? undefined, locale);
sendResponse({ ok: true, result });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
Expand All @@ -398,7 +416,7 @@ export default defineContentScript({
}
if (raw?.type === "automation:repl-overlay") {
if (raw.action === "show") {
showReplOverlay(raw.message ?? undefined);
showReplOverlay(raw.message ?? undefined, locale);
} else if (raw.action === "hide") {
hideReplOverlay();
}
Expand Down
5 changes: 5 additions & 0 deletions apps/chrome-extension/src/entrypoints/options/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type OptionsBindingsArgs = {
autoCliOrderEl: HTMLInputElement;
fontFamilyEl: HTMLInputElement;
fontSizeEl: HTMLInputElement;
uiLocaleEl: HTMLSelectElement;
logsSourceEl: HTMLSelectElement;
logsTailEl: HTMLInputElement;
logsParsedEl: HTMLInputElement;
Expand Down Expand Up @@ -102,6 +103,10 @@ export function bindOptionsInputs({
scheduleAutoSave(200);
});

elements.uiLocaleEl.addEventListener("change", () => {
scheduleAutoSave(0);
});

elements.hoverPromptResetBtn.addEventListener("click", () => {
elements.hoverPromptEl.value = defaultHoverPrompt;
scheduleAutoSave(200);
Expand Down
1 change: 1 addition & 0 deletions apps/chrome-extension/src/entrypoints/options/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function getOptionsElements() {
retriesEl: byId<HTMLInputElement>("retries"),
maxOutputTokensEl: byId<HTMLInputElement>("maxOutputTokens"),
pickersRoot: byId<HTMLDivElement>("pickersRoot"),
uiLocaleEl: byId<HTMLSelectElement>("uiLocale"),
fontFamilyEl: byId<HTMLInputElement>("fontFamily"),
fontSizeEl: byId<HTMLInputElement>("fontSize"),
buildInfoEl: document.getElementById("buildInfo"),
Expand Down
3 changes: 3 additions & 0 deletions apps/chrome-extension/src/entrypoints/options/form-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type FormElements = {
maxOutputTokensEl: HTMLInputElement;
fontFamilyEl: HTMLInputElement;
fontSizeEl: HTMLInputElement;
uiLocaleEl: HTMLSelectElement;
};

type BooleanFormState = {
Expand Down Expand Up @@ -60,6 +61,7 @@ export function buildSavedOptionsSettings({
currentMode: ColorMode;
}): Settings {
return {
uiLocale: elements.uiLocaleEl.value as Settings["uiLocale"],
token: elements.tokenEl.value || defaults.token,
daemonPort: elements.daemonPortEl.value || defaults.daemonPort,
summaryRuntime: booleans.summaryRuntime,
Expand Down Expand Up @@ -157,6 +159,7 @@ export function applyLoadedOptionsSettings({
elements.maxOutputTokensEl.value = settings.maxOutputTokens;
elements.fontFamilyEl.value = settings.fontFamily;
elements.fontSizeEl.value = String(settings.fontSize);
elements.uiLocaleEl.value = settings.uiLocale;

return {
booleans: {
Expand Down
10 changes: 9 additions & 1 deletion apps/chrome-extension/src/entrypoints/options/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<title>Summarize Settings</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<body data-locale-ui>
<main>
<div class="top">
<h1>Summarize Settings</h1>
Expand Down Expand Up @@ -347,6 +347,14 @@ <h2>Daemon</h2>
<h2>UI</h2>
<div class="uiGrid">
<div id="pickersRoot"></div>
<label>
<span>Interface language</span>
<select id="uiLocale">
<option value="auto">Automatic (browser)</option>
<option value="en">English</option>
<option value="tr">Turkish</option>
</select>
</label>
<label>
<span>Font family (CSS)</span>
<input id="fontFamily" type="text" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export function createLogsViewer(options: LogsViewerOptions): LogsViewer {
for (const entry of entries) {
if (!activeLevels.has(entry.level)) continue;
const row = document.createElement("tr");
row.dataset.localeIgnore = "true";
const timeCell = document.createElement("td");
timeCell.textContent = entry.time || "—";
const levelCell = document.createElement("td");
Expand Down
9 changes: 9 additions & 0 deletions apps/chrome-extension/src/entrypoints/options/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { CacheStats } from "@steipete/summarize-core/runtime";
import { applyExtensionLocale, resolveExtensionLocale } from "../../lib/i18n";
import { defaultSettings, loadSettings, saveSettings } from "../../lib/settings";
import { applyTheme, type ColorMode, type ColorScheme } from "../../lib/theme";
import { bindOptionsInputs } from "./bindings";
Expand Down Expand Up @@ -77,6 +78,7 @@ const {
pickersRoot,
fontFamilyEl,
fontSizeEl,
uiLocaleEl,
buildInfoEl,
daemonStatusEl,
browserCacheStatusEl,
Expand Down Expand Up @@ -250,6 +252,7 @@ const settingsElements = {
maxOutputTokensEl,
fontFamilyEl,
fontSizeEl,
uiLocaleEl,
};

const { saveNow, scheduleAutoSave } = createOptionsSaveRuntime({
Expand Down Expand Up @@ -452,6 +455,7 @@ automationPermissionsBtn.addEventListener("click", () => {

async function load() {
const [s] = await Promise.all([loadSettings(), daemonCapability?.initialize()]);
applyExtensionLocale(resolveExtensionLocale(s.uiLocale));
activeProvider = s.provider;
await modelPresets.refreshPresets(s.token);
modelPresets.setValue(s.model);
Expand Down Expand Up @@ -542,6 +546,7 @@ bindOptionsInputs({
autoCliOrderEl,
fontFamilyEl,
fontSizeEl,
uiLocaleEl,
logsSourceEl,
logsTailEl,
logsParsedEl,
Expand All @@ -559,6 +564,10 @@ bindOptionsInputs({
defaultHoverPrompt: defaultSettings.hoverPrompt,
});

uiLocaleEl.addEventListener("change", () => {
applyExtensionLocale(resolveExtensionLocale(uiLocaleEl.value as "auto" | "en" | "tr"));
});

applyBuildInfo(buildInfoEl, {
injectedVersion:
typeof __SUMMARIZE_VERSION__ === "string" && __SUMMARIZE_VERSION__ ? __SUMMARIZE_VERSION__ : "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,11 @@ export function createProcessesViewer(options: ProcessesViewerOptions): Processe
if (selectedId === item.id) row.classList.add("selected");

const toolCell = document.createElement("td");
toolCell.dataset.localeIgnore = "true";
toolCell.textContent = item.label || item.kind || item.command;

const pidCell = document.createElement("td");
pidCell.dataset.localeIgnore = "true";
pidCell.textContent = item.pid ? String(item.pid) : "—";

const statusCell = document.createElement("td");
Expand All @@ -180,12 +182,15 @@ export function createProcessesViewer(options: ProcessesViewerOptions): Processe
elapsedCell.textContent = formatElapsed(item.elapsedMs);

const progressCell = document.createElement("td");
progressCell.dataset.localeIgnore = "true";
progressCell.textContent = formatProgress(item) || "—";

const runCell = document.createElement("td");
runCell.dataset.localeIgnore = "true";
runCell.textContent = item.runId ? item.runId.slice(0, 8) : "—";

const cmdCell = document.createElement("td");
cmdCell.dataset.localeIgnore = "true";
const cmd = buildCommandLabel(item);
cmdCell.textContent = cmd.length > 120 ? `${cmd.slice(0, 120)}…` : cmd;
cmdCell.title = cmd;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type Skill,
saveSkill,
} from "../../automation/skills-store";
import { getActiveExtensionLocale, translateExtensionText } from "../../lib/i18n";

type SkillConflict = { skill: Skill; selected: boolean };

Expand Down Expand Up @@ -49,7 +50,10 @@ export function createSkillsController({
};

const deleteSkillWithPrompt = async (skill: Skill) => {
if (!confirm(`Delete skill "${skill.name}"?`)) return;
if (
!confirm(translateExtensionText(`Delete skill "${skill.name}"?`, getActiveExtensionLocale()))
)
return;
await deleteSkill(skill.name);
editingSkill = null;
await loadSkills();
Expand Down Expand Up @@ -112,14 +116,17 @@ export function createSkillsController({

const name = document.createElement("div");
name.className = "skillName";
name.dataset.localeIgnore = "true";
name.textContent = conflict.skill.name;

const domains = document.createElement("div");
domains.className = "skillDomains";
domains.dataset.localeIgnore = "true";
domains.textContent = conflict.skill.domainPatterns.join(", ");

const desc = document.createElement("div");
desc.className = "skillDescription";
desc.dataset.localeIgnore = "true";
desc.textContent = conflict.skill.shortDescription;

content.append(name, domains, desc);
Expand Down Expand Up @@ -161,6 +168,7 @@ export function createSkillsController({

const heading = document.createElement("div");
heading.className = "skillName";
heading.dataset.localeIgnore = "true";
heading.textContent = `Edit skill: ${editingSkill.name}`;

const nameLabel = document.createElement("label");
Expand Down Expand Up @@ -272,16 +280,19 @@ export function createSkillsController({

const name = document.createElement("div");
name.className = "skillName";
name.dataset.localeIgnore = "true";
name.textContent = skill.name;

const domains = document.createElement("div");
domains.className = "skillDomains";
domains.dataset.localeIgnore = "true";
domains.textContent = skill.domainPatterns.join(", ");

header.append(name, domains);

const desc = document.createElement("div");
desc.className = "skillDescription";
desc.dataset.localeIgnore = "true";
desc.textContent = skill.shortDescription;

const actions = document.createElement("div");
Expand Down
11 changes: 11 additions & 0 deletions apps/chrome-extension/src/entrypoints/sidepanel/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,13 @@ export function bindSettingsStorage({
panelState,
dispatchPanelState,
applyChatEnabled,
applyLocale,
hideAutomationNotice,
}: {
panelState: PanelState;
dispatchPanelState?: (action: PanelStateAction) => void;
applyChatEnabled: () => void;
applyLocale: (locale: Settings["uiLocale"]) => void;
hideAutomationNotice: () => void;
}) {
const dispatch = (action: PanelStateAction) => {
Expand All @@ -219,6 +221,15 @@ export function bindSettingsStorage({
},
});
}
const nextUiLocale = (nextSettings as Partial<Settings>).uiLocale;
const previousUiLocale = (changes.settings?.oldValue as Partial<Settings> | undefined)
?.uiLocale;
if (
(nextUiLocale === "en" || nextUiLocale === "tr" || nextUiLocale === "auto") &&
nextUiLocale !== previousUiLocale
) {
applyLocale(nextUiLocale);
}
const nextChatEnabled = (nextSettings as { chatEnabled?: unknown }).chatEnabled;
if (typeof nextChatEnabled === "boolean") {
dispatch({
Expand Down
Loading