Skip to content

Commit bda929b

Browse files
author
aheizi
committed
Merge branch 'main' into support_project_mcp
# Conflicts: # webview-ui/src/i18n/locales/ar/mcp.json # webview-ui/src/i18n/locales/cs/mcp.json # webview-ui/src/i18n/locales/hu/mcp.json # webview-ui/src/i18n/locales/pt/mcp.json # webview-ui/src/i18n/locales/ru/mcp.json
2 parents c201baa + 8265520 commit bda929b

File tree

133 files changed

+4907
-2294
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+4907
-2294
lines changed

.roomodes

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
"slug": "translate",
2323
"name": "Translate",
2424
"roleDefinition": "You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.",
25-
"customInstructions": "When internationalizing and translating content:\n\n# Translation Style and Tone\n- Maintain a direct and concise style that mirrors the tone of the original text\n- Carefully account for colloquialisms and idiomatic expressions in both source and target languages\n- Aim for culturally relevant and meaningful translations rather than literal translations\n- Adapt the formality level to match the original content (whether formal or informal)\n- Preserve the personality and voice of the original content\n- Use natural-sounding language that feels native to speakers of the target language\n- Don't translate the word \"token\" as it means something specific in English that all languages will understand\n\n# Technical Implementation\n- Use namespaces to organize translations logically\n- Handle pluralization using i18next's built-in capabilities\n- Implement proper interpolation for variables using {{variable}} syntax\n- Don't include defaultValue. The `en` translations are the fallback.\n\n# Quality Assurance\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Watch for placeholders and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- Use context-aware translations when the same string has different meanings\n\n# Supported Languages\n- Localize all strings into the following locale files: ar, ca, cs, de, en, es, fr, hi, hu, it, ja, ko, pl, pt, pt-BR, ru, tr, zh-CN, zh-TW",
25+
"customInstructions": "When internationalizing and translating content:\n\n# Translation Style and Tone\n- Maintain a direct and concise style that mirrors the tone of the original text\n- Carefully account for colloquialisms and idiomatic expressions in both source and target languages\n- Aim for culturally relevant and meaningful translations rather than literal translations\n- Adapt the formality level to match the original content (whether formal or informal)\n- Preserve the personality and voice of the original content\n- Use natural-sounding language that feels native to speakers of the target language\n- Don't translate the word \"token\" as it means something specific in English that all languages will understand\n\n# Technical Implementation\n- Use namespaces to organize translations logically\n- Handle pluralization using i18next's built-in capabilities\n- Implement proper interpolation for variables using {{variable}} syntax\n- Don't include defaultValue. The `en` translations are the fallback.\n- Always use apply_diff instead of write_to_file when editing existing translation files as it's much faster and more reliable\n- When using apply_diff, make sure to carefully identify the exact JSON structure to edit to avoid syntax errors\n- Always use the Trans component for text with embedded components\n\n# Quality Assurance\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Watch for placeholders and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- Use context-aware translations when the same string has different meanings\n- Always validate your translation work by running the missing translations script:\n ```\n node scripts/find-missing-translations.js\n ```\n- Before completing any translation task, ensure there are no missing translations by running the script with the target locale(s):\n ```\n node scripts/find-missing-translations.js --locale=<locale-code>\n ```\n- Address any missing translations identified by the script to ensure complete coverage across all locales\n\n# Supported Languages\n- Localize all strings into the following locale files: ca, de, en, es, fr, hi, it, ja, ko, pl, pt-BR, tr, vi, zh-CN, zh-TW\n- The translation files are under webview-ui/src/i18n/locales/",
2626
"groups": [
2727
"read",
28+
"command",
2829
[
2930
"edit",
3031
{

knip.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"e2e/**",
1717
"src/activate/**",
1818
"src/exports/**",
19-
"src/extension.ts"
19+
"src/extension.ts",
20+
"scripts/**"
2021
],
2122
"workspaces": {
2223
"webview-ui": {
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/**
2+
* Script to find missing translations in locale files
3+
*
4+
* Usage:
5+
* node scripts/find-missing-translations.js [options]
6+
*
7+
* Options:
8+
* --locale=<locale> Only check a specific locale (e.g. --locale=fr)
9+
* --file=<file> Only check a specific file (e.g. --file=chat.json)
10+
* --help Show this help message
11+
*/
12+
13+
const fs = require("fs")
14+
const path = require("path")
15+
16+
// Process command line arguments
17+
const args = process.argv.slice(2).reduce((acc, arg) => {
18+
if (arg === "--help") {
19+
acc.help = true
20+
} else if (arg.startsWith("--locale=")) {
21+
acc.locale = arg.split("=")[1]
22+
} else if (arg.startsWith("--file=")) {
23+
acc.file = arg.split("=")[1]
24+
}
25+
return acc
26+
}, {})
27+
28+
// Show help if requested
29+
if (args.help) {
30+
console.log(`
31+
Find Missing Translations
32+
33+
A utility script to identify missing translations across locale files.
34+
Compares non-English locale files to the English ones to find any missing keys.
35+
36+
Usage:
37+
node scripts/find-missing-translations.js [options]
38+
39+
Options:
40+
--locale=<locale> Only check a specific locale (e.g. --locale=fr)
41+
--file=<file> Only check a specific file (e.g. --file=chat.json)
42+
--help Show this help message
43+
44+
Output:
45+
- Generates a report of missing translations
46+
`)
47+
process.exit(0)
48+
}
49+
50+
// Path to the locales directory
51+
const LOCALES_DIR = path.join(__dirname, "../webview-ui/src/i18n/locales")
52+
53+
// Recursively find all keys in an object
54+
function findKeys(obj, parentKey = "") {
55+
let keys = []
56+
57+
for (const [key, value] of Object.entries(obj)) {
58+
const currentKey = parentKey ? `${parentKey}.${key}` : key
59+
60+
if (typeof value === "object" && value !== null) {
61+
// If value is an object, recurse
62+
keys = [...keys, ...findKeys(value, currentKey)]
63+
} else {
64+
// If value is a primitive, add the key
65+
keys.push(currentKey)
66+
}
67+
}
68+
69+
return keys
70+
}
71+
72+
// Get value at a dotted path in an object
73+
function getValueAtPath(obj, path) {
74+
const parts = path.split(".")
75+
let current = obj
76+
77+
for (const part of parts) {
78+
if (current === undefined || current === null) {
79+
return undefined
80+
}
81+
current = current[part]
82+
}
83+
84+
return current
85+
}
86+
87+
// Main function to find missing translations
88+
function findMissingTranslations() {
89+
try {
90+
// Get all locale directories (or filter to the specified locale)
91+
const allLocales = fs.readdirSync(LOCALES_DIR).filter((item) => {
92+
const stats = fs.statSync(path.join(LOCALES_DIR, item))
93+
return stats.isDirectory() && item !== "en" // Exclude English as it's our source
94+
})
95+
96+
// Filter to the specified locale if provided
97+
const locales = args.locale ? allLocales.filter((locale) => locale === args.locale) : allLocales
98+
99+
if (args.locale && locales.length === 0) {
100+
console.error(`Error: Locale '${args.locale}' not found in ${LOCALES_DIR}`)
101+
process.exit(1)
102+
}
103+
104+
console.log(`Checking ${locales.length} non-English locale(s): ${locales.join(", ")}`)
105+
106+
// Get all English JSON files
107+
const englishDir = path.join(LOCALES_DIR, "en")
108+
let englishFiles = fs.readdirSync(englishDir).filter((file) => file.endsWith(".json") && !file.startsWith("."))
109+
110+
// Filter to the specified file if provided
111+
if (args.file) {
112+
if (!englishFiles.includes(args.file)) {
113+
console.error(`Error: File '${args.file}' not found in ${englishDir}`)
114+
process.exit(1)
115+
}
116+
englishFiles = englishFiles.filter((file) => file === args.file)
117+
}
118+
119+
// Load file contents
120+
const englishFileContents = englishFiles.map((file) => ({
121+
name: file,
122+
content: JSON.parse(fs.readFileSync(path.join(englishDir, file), "utf8")),
123+
}))
124+
125+
console.log(
126+
`Checking ${englishFileContents.length} translation file(s): ${englishFileContents.map((f) => f.name).join(", ")}`,
127+
)
128+
129+
// Results object to store missing translations
130+
const missingTranslations = {}
131+
132+
// For each locale, check for missing translations
133+
for (const locale of locales) {
134+
missingTranslations[locale] = {}
135+
136+
for (const { name, content: englishContent } of englishFileContents) {
137+
const localeFilePath = path.join(LOCALES_DIR, locale, name)
138+
139+
// Check if the file exists in the locale
140+
if (!fs.existsSync(localeFilePath)) {
141+
missingTranslations[locale][name] = { file: "File is missing entirely" }
142+
continue
143+
}
144+
145+
// Load the locale file
146+
const localeContent = JSON.parse(fs.readFileSync(localeFilePath, "utf8"))
147+
148+
// Find all keys in the English file
149+
const englishKeys = findKeys(englishContent)
150+
151+
// Check for missing keys in the locale file
152+
const missingKeys = []
153+
154+
for (const key of englishKeys) {
155+
const englishValue = getValueAtPath(englishContent, key)
156+
const localeValue = getValueAtPath(localeContent, key)
157+
158+
if (localeValue === undefined) {
159+
missingKeys.push({
160+
key,
161+
englishValue,
162+
})
163+
}
164+
}
165+
166+
if (missingKeys.length > 0) {
167+
missingTranslations[locale][name] = missingKeys
168+
}
169+
}
170+
}
171+
172+
// Output results
173+
let hasMissingTranslations = false
174+
175+
console.log("\nMissing Translations Report:\n")
176+
177+
for (const [locale, files] of Object.entries(missingTranslations)) {
178+
if (Object.keys(files).length === 0) {
179+
console.log(`✅ ${locale}: No missing translations`)
180+
continue
181+
}
182+
183+
hasMissingTranslations = true
184+
console.log(`📝 ${locale}:`)
185+
186+
for (const [fileName, missingItems] of Object.entries(files)) {
187+
if (missingItems.file) {
188+
console.log(` - ${fileName}: ${missingItems.file}`)
189+
continue
190+
}
191+
192+
console.log(` - ${fileName}: ${missingItems.length} missing translations`)
193+
194+
for (const { key, englishValue } of missingItems) {
195+
console.log(` ${key}: "${englishValue}"`)
196+
}
197+
}
198+
199+
console.log("")
200+
}
201+
202+
if (!hasMissingTranslations) {
203+
console.log("\n✅ All translations are complete!")
204+
} else {
205+
console.log("✏️ To add missing translations:")
206+
console.log("1. Add the missing keys to the corresponding locale files")
207+
console.log("2. Translate the English values to the appropriate language")
208+
console.log("3. Run this script again to verify all translations are complete")
209+
}
210+
} catch (error) {
211+
console.error("Error:", error.message)
212+
console.error(error.stack)
213+
process.exit(1)
214+
}
215+
}
216+
217+
// Run the main function
218+
findMissingTranslations()

src/core/webview/ClineProvider.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
15491549
await this.updateGlobalState("browserToolEnabled", message.bool ?? true)
15501550
await this.postStateToWebview()
15511551
break
1552+
case "language":
1553+
await this.updateGlobalState("language", message.text)
1554+
await this.postStateToWebview()
1555+
break
15521556
case "showRooIgnoredFiles":
15531557
await this.updateGlobalState("showRooIgnoredFiles", message.bool ?? true)
15541558
await this.postStateToWebview()
@@ -2528,8 +2532,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
25282532
writeDelayMs: stateValues.writeDelayMs ?? 1000,
25292533
terminalOutputLineLimit: stateValues.terminalOutputLineLimit ?? 500,
25302534
mode: stateValues.mode ?? defaultModeSlug,
2531-
// Pass the VSCode language code directly
2532-
language: formatLanguage(vscode.env.language),
2535+
language: stateValues.language || formatLanguage(vscode.env.language),
25332536
mcpEnabled: stateValues.mcpEnabled ?? true,
25342537
enableMcpServerCreation: stateValues.enableMcpServerCreation ?? true,
25352538
alwaysApproveResubmit: stateValues.alwaysApproveResubmit ?? false,
@@ -2641,7 +2644,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26412644
* like the current mode, API provider, etc.
26422645
*/
26432646
public async getTelemetryProperties(): Promise<Record<string, any>> {
2644-
const { mode, apiConfiguration } = await this.getState()
2647+
const { mode, apiConfiguration, language } = await this.getState()
26452648
const appVersion = this.context.extension?.packageJSON?.version
26462649
const vscodeVersion = vscode.version
26472650
const platform = process.platform
@@ -2656,6 +2659,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
26562659
properties.appVersion = appVersion
26572660
}
26582661

2662+
// Add language
2663+
if (language) {
2664+
properties.language = language
2665+
}
2666+
26592667
// Add current mode
26602668
if (mode) {
26612669
properties.mode = mode

src/exports/roo-code.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ export type GlobalStateKey =
218218
| "telemetrySetting"
219219
| "showRooIgnoredFiles"
220220
| "remoteBrowserEnabled"
221+
| "language"
221222

222223
export type ConfigurationKey = GlobalStateKey | SecretKey
223224

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export interface WebviewMessage {
108108
| "discoverBrowser"
109109
| "browserConnectionResult"
110110
| "remoteBrowserEnabled"
111+
| "language"
111112
text?: string
112113
disabled?: boolean
113114
askResponse?: ClineAskResponse

src/shared/__tests__/experiments.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ describe("experiments", () => {
55
it("is configured correctly", () => {
66
expect(EXPERIMENT_IDS.POWER_STEERING).toBe("powerSteering")
77
expect(experimentConfigsMap.POWER_STEERING).toMatchObject({
8-
name: 'Use experimental "power steering" mode',
9-
description:
10-
"When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.",
118
enabled: false,
129
})
1310
})

src/shared/experiments.ts

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,25 @@ export type ExperimentKey = keyof typeof EXPERIMENT_IDS
1010
export type ExperimentId = valueof<typeof EXPERIMENT_IDS>
1111

1212
export interface ExperimentConfig {
13-
name: string
14-
description: string
1513
enabled: boolean
1614
}
1715

1816
type valueof<X> = X[keyof X]
1917

2018
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
2119
DIFF_STRATEGY: {
22-
name: "Use experimental unified diff strategy",
23-
description:
24-
"Enable the experimental unified diff strategy. This strategy might reduce the number of retries caused by model errors but may cause unexpected behavior or incorrect edits. Only enable if you understand the risks and are willing to carefully review all changes.",
2520
enabled: false,
2621
},
2722
SEARCH_AND_REPLACE: {
28-
name: "Use experimental search and replace tool",
29-
description:
30-
"Enable the experimental search and replace tool, allowing Roo to replace multiple instances of a search term in one request.",
3123
enabled: false,
3224
},
3325
INSERT_BLOCK: {
34-
name: "Use experimental insert content tool",
35-
36-
description:
37-
"Enable the experimental insert content tool, allowing Roo to insert content at specific line numbers without needing to create a diff.",
3826
enabled: false,
3927
},
4028
POWER_STEERING: {
41-
name: 'Use experimental "power steering" mode',
42-
description:
43-
"When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.",
4429
enabled: false,
4530
},
4631
MULTI_SEARCH_AND_REPLACE: {
47-
name: "Use experimental multi block diff tool",
48-
description:
49-
"When enabled, Roo will use multi block diff tool. This will try to update multiple code blocks in the file in one request.",
5032
enabled: false,
5133
},
5234
}
@@ -67,17 +49,4 @@ export const experiments = {
6749
},
6850
} as const
6951

70-
// Expose experiment details for UI - pre-compute from map for better performance
71-
export const experimentLabels = Object.fromEntries(
72-
Object.entries(experimentConfigsMap).map(([_, config]) => [
73-
EXPERIMENT_IDS[_ as keyof typeof EXPERIMENT_IDS] as ExperimentId,
74-
config.name,
75-
]),
76-
) as Record<string, string>
77-
78-
export const experimentDescriptions = Object.fromEntries(
79-
Object.entries(experimentConfigsMap).map(([_, config]) => [
80-
EXPERIMENT_IDS[_ as keyof typeof EXPERIMENT_IDS] as ExperimentId,
81-
config.description,
82-
]),
83-
) as Record<string, string>
52+
// No longer needed as we use translation keys directly in the UI

src/shared/globalState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const GLOBAL_STATE_KEYS = [
116116
"telemetrySetting",
117117
"showRooIgnoredFiles",
118118
"remoteBrowserEnabled",
119+
"language",
119120
"maxWorkspaceFiles",
120121
] as const
121122

src/shared/tool-groups.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,3 @@ export function getToolName(toolConfig: string | readonly [ToolName, ...any[]]):
6666
export function getToolOptions(toolConfig: string | readonly [ToolName, ...any[]]): any {
6767
return typeof toolConfig === "string" ? undefined : toolConfig[1]
6868
}
69-
70-
// Display names for groups in UI
71-
export const GROUP_DISPLAY_NAMES: Record<ToolGroup, string> = {
72-
read: "Read Files",
73-
edit: "Edit Files",
74-
browser: "Use Browser",
75-
command: "Run Commands",
76-
mcp: "Use MCP",
77-
}

0 commit comments

Comments
 (0)