Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

localize Theia AI strings #14857

Merged
merged 5 commits into from
Feb 18, 2025
Merged
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
8 changes: 5 additions & 3 deletions packages/ai-anthropic/src/browser/anthropic-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution';
import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences';
import { nls } from '@theia/core';

export const API_KEY_PREF = 'ai-features.anthropic.AnthropicApiKey';
export const MODELS_PREF = 'ai-features.anthropic.AnthropicModels';
Expand All @@ -25,13 +26,14 @@ export const AnthropicPreferencesSchema: PreferenceSchema = {
properties: {
[API_KEY_PREF]: {
type: 'string',
markdownDescription: 'Enter an API Key of your official Anthropic Account. **Please note:** By using this preference the Anthropic API key will be stored in clear text\
on the machine running Theia. Use the environment variable `ANTHROPIC_API_KEY` to set the key securely.',
markdownDescription: nls.localize('theia/ai/anthropic/apiKey/description',
'Enter an API Key of your official Anthropic Account. **Please note:** By using this preference the Anthropic API key will be stored in clear text\
on the machine running Theia. Use the environment variable `ANTHROPIC_API_KEY` to set the key securely.'),
title: AI_CORE_PREFERENCES_TITLE,
},
[MODELS_PREF]: {
type: 'array',
description: 'Official Anthropic models to use',
description: nls.localize('theia/ai/anthropic/models/description', 'Official Anthropic models to use'),
title: AI_CORE_PREFERENCES_TITLE,
default: ['claude-3-5-sonnet-latest', 'claude-3-5-haiku-latest', 'claude-3-opus-latest'],
items: {
Expand Down
12 changes: 6 additions & 6 deletions packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// *****************************************************************************

import { inject, injectable } from '@theia/core/shared/inversify';
import { CommandRegistry, isOSX, QuickInputButton, QuickInputService, QuickPickItem } from '@theia/core';
import { CommandRegistry, isOSX, nls, QuickInputButton, QuickInputService, QuickPickItem } from '@theia/core';
import { Widget } from '@theia/core/lib/browser';
import { AI_CHAT_NEW_CHAT_WINDOW_COMMAND, AI_CHAT_SHOW_CHATS_COMMAND, ChatCommands } from './chat-view-commands';
import { ChatAgentLocation, ChatService } from '@theia/ai-chat';
Expand All @@ -37,7 +37,7 @@ export class AIChatContribution extends AbstractViewContribution<ChatViewWidget>

protected static readonly REMOVE_CHAT_BUTTON: QuickInputButton = {
iconClass: 'codicon-remove-close',
tooltip: 'Remove Chat',
tooltip: nls.localize('theia/ai/chat-ui/removeChat', 'Remove Chat'),
};

@inject(SecondaryWindowHandler)
Expand Down Expand Up @@ -90,13 +90,13 @@ export class AIChatContribution extends AbstractViewContribution<ChatViewWidget>
registry.registerItem({
id: AI_CHAT_NEW_CHAT_WINDOW_COMMAND.id,
command: AI_CHAT_NEW_CHAT_WINDOW_COMMAND.id,
tooltip: 'New Chat',
tooltip: nls.localizeByDefault('New Chat'),
isVisible: widget => this.isChatViewWidget(widget)
});
registry.registerItem({
id: AI_CHAT_SHOW_CHATS_COMMAND.id,
command: AI_CHAT_SHOW_CHATS_COMMAND.id,
tooltip: 'Show Chats...',
tooltip: nls.localizeByDefault('Show Chats...'),
isVisible: widget => this.isChatViewWidget(widget),
});
}
Expand All @@ -122,14 +122,14 @@ export class AIChatContribution extends AbstractViewContribution<ChatViewWidget>
protected askForChatSession(): Promise<QuickPickItem | undefined> {
const getItems = () =>
this.chatService.getSessions().filter(session => !session.isActive).map(session => <QuickPickItem>({
label: session.title ?? 'New Chat',
label: session.title ?? nls.localizeByDefault('New Chat'),
id: session.id,
buttons: [AIChatContribution.REMOVE_CHAT_BUTTON]
})).reverse();

const defer = new Deferred<QuickPickItem | undefined>();
const quickPick = this.quickInputService.createQuickPick();
quickPick.placeholder = 'Select chat';
quickPick.placeholder = nls.localize('theia/ai/chat-ui/selectChat', 'Select chat');
quickPick.canSelectMany = false;
quickPick.items = getItems();

Expand Down
43 changes: 29 additions & 14 deletions packages/ai-chat-ui/src/browser/chat-input-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { ChangeSet, ChangeSetElement, ChatAgent, ChatChangeEvent, ChatModel, ChatRequestModel } from '@theia/ai-chat';
import { Disposable, UntitledResourceResolver } from '@theia/core';
import { Disposable, nls, UntitledResourceResolver } from '@theia/core';
import { ContextMenuRenderer, LabelProvider, Message, ReactWidget } from '@theia/core/lib/browser';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
Expand Down Expand Up @@ -355,14 +355,14 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
const leftOptions = [
...(props.showContext
? [{
title: 'Attach elements to context',
title: nls.localize('theia/ai/chat-ui/attachToContext', 'Attach elements to context'),
handler: () => { /* TODO */ },
className: 'codicon-add'
}]
: []),
...(props.showPinnedAgent
? [{
title: props.pinnedAgent ? 'Unpin Agent' : 'Pin Agent',
title: props.pinnedAgent ? nls.localize('theia/ai/chat-ui/unpinAgent', 'Unpin Agent') : nls.localize('theia/ai/chat-ui/pinAgent', 'Pin Agent'),
handler: props.pinnedAgent ? props.onUnpin : handlePin,
className: 'at-icon',
text: {
Expand All @@ -375,7 +375,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu

const rightOptions = inProgress
? [{
title: 'Cancel (Esc)',
title: nls.localize('theia/ai/chat-ui/cancel', 'Cancel (Esc)'),
handler: () => {
const latestRequest = getLatestRequest(props.chatModel);
if (latestRequest) {
Expand All @@ -386,7 +386,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
className: 'codicon-stop-circle'
}]
: [{
title: 'Send (Enter)',
title: nls.localize('theia/ai/chat-ui/send', 'Send (Enter)'),
handler: () => {
if (props.isEnabled) {
submit(editorRef.current?.document.textEditorModel.getValue() || '');
Expand All @@ -402,7 +402,7 @@ const ChatInput: React.FunctionComponent<ChatInputProperties> = (props: ChatInpu
}
<div className='theia-ChatInput-Editor-Box'>
<div className='theia-ChatInput-Editor' ref={editorContainerRef} onKeyDown={onKeyDown} onFocus={handleInputFocus} onBlur={handleInputBlur}>
<div ref={placeholderRef} className='theia-ChatInput-Editor-Placeholder'>Ask a question</div>
<div ref={placeholderRef} className='theia-ChatInput-Editor-Placeholder'>{nls.localizeByDefault('Ask a question')}</div>
</div>
<ChatInputOptions leftOptions={leftOptions} rightOptions={rightOptions} />
</div>
Expand Down Expand Up @@ -460,18 +460,18 @@ const ChangeSetBox: React.FunctionComponent<{ changeSet: ChangeSetUI }> = ({ cha
<button
className='theia-button'
disabled={changeSet.disabled}
title='Accept all pending changes'
title={nls.localize('theia/ai/chat-ui/acceptAll', 'Accept all pending changes')}
onClick={() => changeSet.acceptAllPendingElements()}
>
Accept
</button>
<span className='codicon codicon-close action' title='Delete Change Set' onClick={() => changeSet.delete()} />
<span className='codicon codicon-close action' title={nls.localize('theia/ai/chat-ui/deleteChangeSet', 'Delete Change Set')} onClick={() => changeSet.delete()} />
</div>
</div>
<div className='theia-ChatInput-ChangeSet-List'>
<ul>
{changeSet.elements.map((element, index) => (
<li key={index} title='Open Diff' onClick={() => element.openChange?.()}>
<li key={index} title={nls.localize('theia/ai/chat-ui/openDiff', 'Open Diff')} onClick={() => element.openChange?.()}>
<div className={`theia-ChatInput-ChangeSet-Icon ${element.iconClass}`} />
<span className='theia-ChatInput-ChangeSet-labelParts'>
<span className={`theia-ChatInput-ChangeSet-title ${element.nameClass}`}>
Expand All @@ -482,10 +482,25 @@ const ChangeSetBox: React.FunctionComponent<{ changeSet: ChangeSetUI }> = ({ cha
</span>
</span>
<div className='theia-ChatInput-ChangeSet-Actions'>
{element.open && (<span className='codicon codicon-file action' title='Open Original File' onClick={noPropagation(() => element.open!())} />)}
{element.discard && (<span className='codicon codicon-discard action' title='Undo' onClick={noPropagation(() => element.discard!())} />)}
{element.accept && (<span className='codicon codicon-check action' title='Accept' onClick={noPropagation(() => element.accept!())} />)}
<span className='codicon codicon-close action' title='Delete' onClick={noPropagation(() => element.delete())} />
{element.open && (
<span
className='codicon codicon-file action'
title={nls.localize('theia/ai/chat-ui/openOriginalFile', 'Open Original File')}
onClick={noPropagation(() => element.open!())}
/>)}
{element.discard && (
<span
className='codicon codicon-discard action'
title={nls.localizeByDefault('Undo')}
onClick={noPropagation(() => element.discard!())}
/>)}
{element.accept && (
<span
className='codicon codicon-check action'
title={nls.localizeByDefault('Accept')}
onClick={noPropagation(() => element.accept!())}
/>)}
<span className='codicon codicon-close action' title={nls.localizeByDefault('Delete')} onClick={noPropagation(() => element.delete())} />
</div>
</li>
))}
Expand Down Expand Up @@ -534,7 +549,7 @@ const ChatInputOptions: React.FunctionComponent<ChatInputOptionsProps> = ({ left
onClick={option.handler}
>
<span>{option.text?.content}</span>
<span className={`codicon ${option.className}`}/>
<span className={`codicon ${option.className}`} />
</span>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import { ReactNode } from '@theia/core/shared/react';
import { nls } from '@theia/core/lib/common/nls';
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
Expand Down Expand Up @@ -113,7 +114,7 @@ export class CodePartRenderer
private getTitle(uri: URI | undefined, language: string | undefined): string {
// If there is a URI, use the file name as the title. Otherwise, use the language as the title.
// If there is no language, use a generic fallback title.
return uri?.path?.toString().split('/').pop() ?? language ?? 'Generated Code';
return uri?.path?.toString().split('/').pop() ?? language ?? nls.localize('theia/ai/chat-ui/code-part-renderer/generatedCode', 'Generated Code');
}

/**
Expand Down Expand Up @@ -157,7 +158,7 @@ const CopyToClipboardButton = (props: { code: string, clipboardService: Clipboar
const copyCodeToClipboard = React.useCallback(() => {
clipboardService.writeText(code);
}, [code, clipboardService]);
return <div className='button codicon codicon-copy' title='Copy' role='button' onClick={copyCodeToClipboard}></div>;
return <div className='button codicon codicon-copy' title={nls.localizeByDefault('Copy')} role='button' onClick={copyCodeToClipboard}></div>;
};

@injectable()
Expand Down Expand Up @@ -189,7 +190,7 @@ const InsertCodeAtCursorButton = (props: { code: string, editorManager: EditorMa
}]);
}
}, [code, editorManager]);
return <div className='button codicon codicon-insert' title='Insert at Cursor' role='button' onClick={insertCode}></div>;
return <div className='button codicon codicon-insert' title={nls.localizeByDefault('Insert at Cursor')} role='button' onClick={insertCode}></div>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
import { injectable } from '@theia/core/shared/inversify';
import { ChatResponseContent } from '@theia/ai-chat/lib/common';
import { ReactNode } from '@theia/core/shared/react';
import { nls } from '@theia/core/lib/common/nls';
import * as React from '@theia/core/shared/react';

@injectable()
Expand All @@ -30,6 +31,8 @@ export class TextPartRenderer implements ChatResponsePartRenderer<ChatResponseCo
if (response && ChatResponseContent.hasAsString(response)) {
return <span>{response.asString()}</span>;
}
return <span>Can't display response, please check your ChatResponsePartRenderers! {JSON.stringify(response)}</span>;
return <span>
{nls.localize('theia/ai/chat-ui/text-part-renderer/cantDisplay',
"Can't display response, please check your ChatResponsePartRenderers!")} {JSON.stringify(response)}</span>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
import { injectable } from '@theia/core/shared/inversify';
import { ChatResponseContent, ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
import { ReactNode } from '@theia/core/shared/react';
import { nls } from '@theia/core/lib/common/nls';
import * as React from '@theia/core/shared/react';

@injectable()
Expand All @@ -35,14 +36,14 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
<h4 className='theia-toolCall'>
{response.finished ? (
<details>
<summary>Ran {response.name}
<summary>{nls.localize('theia/ai/chat-ui/toolcall-part-renderer/finished', 'Ran')} {response.name}
({this.renderCollapsibleArguments(response.arguments)})
</summary>
<pre>{this.tryPrettyPrintJson(response)}</pre>
</details>
) : (
<span>
<Spinner /> Running {response.name}({this.renderCollapsibleArguments(response.arguments)})
<Spinner /> {nls.localizeByDefault('Running')} {response.name}({this.renderCollapsibleArguments(response.arguments)})
</span>
)}
</h4>
Expand Down Expand Up @@ -82,7 +83,12 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
}
} catch (e) {
if (typeof responseContent !== 'string') {
responseContent = `The content could not be converted to string: '${e.message}'. This is the original content: '${responseContent}'.`;
responseContent = nls.localize(
'theia/ai/chat-ui/toolcall-part-renderer/prettyPrintError',
"The content could not be converted to string: '{0}'. This is the original content: '{1}'.",
e.message,
responseContent
);
}
// fall through
}
Expand Down
Loading
Loading