Skip to content

Add translations #128

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
76 changes: 76 additions & 0 deletions locale/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"Chat and completer provider": "Fournisseur de chat et de compléteur",
"Chat provider": "Fournisseur de chat",
"Completer provider": "Fournisseur de compléteur",
"The provider registry is needed to enable the jupyterlite-ai settings panel": "Le registre des fournisseurs est nécessaire pour activer le panneau des paramètres de jupyterlite-ai",
"The provider is missing from the completer settings": "Le fournisseur est manquant dans les paramètres du compléteur",
"The AI provider to use for chat and completion": "Le fournisseur d'IA à utiliser pour le chat et la complétion",
"The provider is missing from the chat settings": "Le fournisseur est manquant dans les paramètres du chat",
"Instructions": "Instructions",
"Restore to Defaults": "Restaurer les valeurs par défaut",
"Stop streaming": "Arrêter le streaming",
"#### Ask JupyterLite AI": "#### Demander à JupyterLite AI",
"The provider to use can be set in the": "Le fournisseur à utiliser peut être défini dans l'",
"settings editor": "éditeur de paramètres",
"by selecting it from the": "en le sélectionnant depuis les",
"_AI provider_ settings": "paramètres _Fournisseur d'IA_",
"The current providers that are available are": "Les fournisseurs actuellement disponibles sont",
"To clear the chat, you can use the": "Pour effacer le chat, vous pouvez utiliser la",
"command from the chat input": "commande depuis l'entrée du chat",
"AI provider not configured": "Fournisseur d'IA non configuré",
"/clear": "/effacer",
"Clear the chat": "Effacer le chat",
"A AI provider named '%1' is already registered": "Un fournisseur d'IA nommé '%1' est déjà enregistré",
"Autocompletion registry": "Registre d'autocomplétion",
"LLM chat extension": "Extension de chat LLM",
"The SettingsRegistry is not loaded for the chat extension": "Le SettingsRegistry n'est pas chargé pour l'extension de chat",
"Something went wrong when reading the settings.\n%1": "Quelque chose s'est mal passé lors de la lecture des paramètres.\n%1",
"Jupyterlite AI Chat": "Chat JupyterLite AI",
"Failed to load settings for %1": "Échec du chargement des paramètres pour %1",
"AI provider": "Fournisseur d'IA",
"Provider settings": "Paramètres du fournisseur",
"Support for ChromeAI is still experimental and only available in Google Chrome.": "Le support de ChromeAI est encore expérimental et uniquement disponible dans Google Chrome.",
"You can test ChromeAI is enabled in your browser by going to the following URL:": "Vous pouvez tester si ChromeAI est activé dans votre navigateur en accédant à l'URL suivante :",
"Enable the proper flags in Google Chrome.": "Activez les drapeaux appropriés dans Google Chrome.",
"Then restart Chrome for these changes to take effect.": "Redémarrez ensuite Chrome pour que ces modifications prennent effet.",
"On first use, Chrome will download the on-device model, which can be as large as 22GB (according to their docs and at the time of writing).": "À la première utilisation, Chrome téléchargera le modèle sur appareil, qui peut faire jusqu'à 22 Go (selon leur documentation et au moment de l'écriture).",
"During the download, ChromeAI may not be available via the extension.": "Pendant le téléchargement, ChromeAI peut ne pas être disponible via l'extension.",
"For more information about Chrome Built-in AI:": "Pour plus d'informations sur l'IA intégrée de Chrome :",
"Your browser does not support ChromeAI. Please use an updated chrome based browser like Google Chrome, and follow the instructions in settings to enable it.": "Votre navigateur ne supporte pas ChromeAI. Veuillez utiliser un navigateur basé sur Chrome à jour comme Google Chrome, et suivez les instructions dans les paramètres pour l'activer.",
"The ChromeAI model is not available in your browser. Please ensure you have enabled the necessary flags in Google Chrome as described in the instructions in settings.": "Le modèle ChromeAI n'est pas disponible dans votre navigateur. Veuillez vous assurer d'avoir activé les drapeaux nécessaires dans Google Chrome comme décrit dans les instructions des paramètres.",
"This extension is still very much experimental. It is not an official Google extension.": "Cette extension est encore très expérimentale. Ce n'est pas une extension officielle de Google.",
"Go to <https://aistudio.google.com> and create an API key.": "Allez sur <https://aistudio.google.com> et créez une clé API.",
"Open the JupyterLab settings and go to the **Ai providers** section to select the `Gemini` provider and add your API key (required).": "Ouvrez les paramètres de JupyterLab et allez dans la section **Fournisseurs d'IA** pour sélectionner le fournisseur `Gemini` et ajouter votre clé API (obligatoire).",
"Open the chat, or use the inline completer.": "Ouvrez le chat, ou utilisez le compléteur en ligne.",
"This extension is still very much experimental. It is not an official MistralAI extension.": "Cette extension est encore très expérimentale. Ce n'est pas une extension officielle de MistralAI.",
"Go to <https://console.mistral.ai/api-keys/> and create an API key.": "Allez sur <https://console.mistral.ai/api-keys/> et créez une clé API.",
"Open the JupyterLab settings and go to the **Ai providers** section to select the `MistralAI` provider and the API key (required).": "Ouvrez les paramètres de JupyterLab et allez dans la section **Fournisseurs d'IA** pour sélectionner le fournisseur `MistralAI` et la clé API (obligatoire).",
"**Note:** When using MistralAI for completions, only a subset of models are available. Please check [this resource](https://docs.mistral.ai/api/#tag/fim) to see the list of supported models for completions.": "**Note :** Lors de l'utilisation de MistralAI pour les complétions, seul un sous-ensemble de modèles est disponible. Veuillez vérifier [cette ressource](https://docs.mistral.ai/api/#tag/fim) pour voir la liste des modèles supportés pour les complétions.",
"Open the chat, or use the inline completer": "Ouvrez le chat, ou utilisez le compléteur en ligne",
"Ollama allows to run large language models locally on your machine.": "Ollama permet d'exécuter de grands modèles de langage localement sur votre machine.",
"To use it you need to install the Ollama CLI and pull the model you want to use.": "Pour l'utiliser, vous devez installer la CLI Ollama et télécharger le modèle que vous souhaitez utiliser.",
"Install the Ollama CLI by following the instructions at <https://ollama.com/download>": "Installez la CLI Ollama en suivant les instructions sur <https://ollama.com/download>",
"Pull the model you want to use by running the following command in your terminal:": "Téléchargez le modèle que vous souhaitez utiliser en exécutant la commande suivante dans votre terminal :",
"For example, to pull the Llama 2 model, run:": "Par exemple, pour télécharger le modèle Llama 2, exécutez :",
"Once the model is pulled, you can use it in your application by running the following command:": "Une fois le modèle téléchargé, vous pouvez l'utiliser dans votre application en exécutant la commande suivante :",
"This model will be available in the extension, using the model name you used in the command above.": "Ce modèle sera disponible dans l'extension, en utilisant le nom de modèle que vous avez utilisé dans la commande ci-dessus.",
"Deploying Lite/Lab on external server": "Déploiement de Lite/Lab sur un serveur externe",
"See https://objectgraph.com/blog/ollama-cors/ for more details.": "Voir https://objectgraph.com/blog/ollama-cors/ pour plus de détails.",
"On Linux, you can run the following commands:": "Sur Linux, vous pouvez exécuter les commandes suivantes :",
"Check if CORS is enabled on the server. You can do this by running the following command in your terminal:": "Vérifiez si CORS est activé sur le serveur. Vous pouvez le faire en exécutant la commande suivante dans votre terminal :",
"If CORS is disabled, you will see a response like this:": "Si CORS est désactivé, vous verrez une réponse comme celle-ci :",
"If CORS is not enabled, update _/etc/systemd/system/ollama.service_ with:": "Si CORS n'est pas activé, mettez à jour _/etc/systemd/system/ollama.service_ avec :",
"Restart the service:": "Redémarrez le service :",
"Check if CORS is enabled on the server again by running the following command in your terminal:": "Vérifiez à nouveau si CORS est activé sur le serveur en exécutant la commande suivante dans votre terminal :",
"WebLLM enables running LLMs directly in your browser, making it possible to use AI features without sending data to external servers.": "WebLLM permet d'exécuter des LLM directement dans votre navigateur, permettant d'utiliser les fonctionnalités d'IA sans envoyer de données vers des serveurs externes.",
"WebLLM runs models entirely in your browser, so initial model download may be large (100MB-2GB depending on the model).": "WebLLM exécute les modèles entièrement dans votre navigateur, donc le téléchargement initial du modèle peut être important (100MB-2GB selon le modèle).",
"**Requirements:** WebLLM requires a browser with WebGPU support (Chrome 113+, Edge 113+, or Safari 17+). It will not work on older browsers or browsers without WebGPU enabled.": "**Exigences :** WebLLM nécessite un navigateur avec support WebGPU (Chrome 113+, Edge 113+, ou Safari 17+). Il ne fonctionnera pas sur les navigateurs plus anciens ou les navigateurs sans WebGPU activé.",
"Enter a model in the JupyterLab settings under the **Ai providers** section. Select the `WebLLM` provider and type the model you want to use.": "Entrez un modèle dans les paramètres de JupyterLab sous la section **Fournisseurs d'IA**. Sélectionnez le fournisseur `WebLLM` et tapez le modèle que vous souhaitez utiliser.",
"When you first use WebLLM, your browser will download the model. A progress notification will appear:": "Quand vous utilisez WebLLM pour la première fois, votre navigateur téléchargera le modèle. Une notification de progression apparaîtra :",
"Once loaded, use the chat": "Une fois chargé, utilisez le chat",
"Example of available models:": "Exemple de modèles disponibles :",
"See the full list of models: https://github.com/mlc-ai/web-llm/blob/632d34725629b480b5b2772379ef5c150b1286f0/src/config.ts#L303-L309": "Voir la liste complète des modèles : https://github.com/mlc-ai/web-llm/blob/632d34725629b480b5b2772379ef5c150b1286f0/src/config.ts#L303-L309",
"Model performance depends on your device's hardware capabilities. More powerful devices will run models faster. Some larger models may not work well on devices with limited GPU memory or may experience slow response times.": "Les performances du modèle dépendent des capacités matérielles de votre appareil. Les appareils plus puissants exécuteront les modèles plus rapidement. Certains modèles plus grands peuvent ne pas bien fonctionner sur des appareils avec une mémoire GPU limitée ou peuvent subir des temps de réponse lents.",
"Your browser does not support WebLLM, it does not support required WebGPU.": "Votre navigateur ne supporte pas WebLLM, il ne supporte pas WebGPU requis.",
"You may need to enable WebGPU, `await navigator.gpu.requestAdapter()` is null.": "Vous devrez peut-être activer WebGPU, `await navigator.gpu.requestAdapter()` est null."
}
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf,md}",
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
"src/**/*.{ts,tsx}",
"schema/*.json"
"schema/*.json",
"locale/*.json"
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand All @@ -27,13 +28,14 @@
"url": "https://github.com/jupyterlite/ai.git"
},
"scripts": {
"build": "jlpm build:lib && jlpm build:labextension:dev",
"build:dev": "jlpm build:lib && jlpm build:labextension:dev",
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
"build": "jlpm build:lib && jlpm build:labextension:dev && jlpm copy:locale",
"build:dev": "jlpm build:lib && jlpm build:labextension:dev && jlpm copy:locale",
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension && jlpm copy:locale",
"build:labextension": "jupyter labextension build .",
"build:labextension:dev": "jupyter labextension build --development True .",
"build:lib": "tsc --sourceMap",
"build:lib:prod": "tsc",
"copy:locale": "node scripts/copy-locale.js",
"clean": "jlpm clean:lib",
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
Expand Down Expand Up @@ -64,6 +66,7 @@
"@jupyterlab/notebook": "^4.4.0",
"@jupyterlab/rendermime": "^4.4.0",
"@jupyterlab/settingregistry": "^4.4.0",
"@jupyterlab/translation": "^4.4.3",
"@jupyterlab/ui-components": "^4.4.0",
"@langchain/anthropic": "^0.3.24",
"@langchain/community": "^0.3.48",
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ npm = ["jlpm"]
source_dir = "src"
build_dir = "jupyterlite_ai/labextension"

[tool.hatch.build.hooks.custom]
path = "scripts/copy-locale.js"

[tool.jupyter-releaser.options]
version_cmd = "hatch version"

Expand Down
37 changes: 37 additions & 0 deletions scripts/copy-locale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

// Paths
const sourcePath = path.join(__dirname, '..', 'locale');
const targetPath = path.join(__dirname, '..', 'jupyterlite_ai', 'labextension', 'locale');

// Function to copy directory recursively
function copyDir(src, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}

const files = fs.readdirSync(src);

files.forEach(file => {
const srcFile = path.join(src, file);
const destFile = path.join(dest, file);

if (fs.statSync(srcFile).isDirectory()) {
copyDir(srcFile, destFile);
} else {
fs.copyFileSync(srcFile, destFile);
}
});
}

// Copy locale files
if (fs.existsSync(sourcePath)) {
console.log('Copying locale files...');
copyDir(sourcePath, targetPath);
console.log('✅ Locale files copied successfully');
} else {
console.log('❌ Source locale directory not found');
}
50 changes: 34 additions & 16 deletions src/chat-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
SystemMessage
} from '@langchain/core/messages';
import { UUID } from '@lumino/coreutils';
import { TranslationBundle } from '@jupyterlab/translation';

import { DEFAULT_CHAT_SYSTEM_PROMPT } from './default-prompts';
import { jupyternautLiteIcon } from './icons';
Expand All @@ -35,16 +36,19 @@ import { AIChatModel } from './types/ai-model';
const AI_AVATAR_BASE64 = btoa(jupyternautLiteIcon.svgstr);
const AI_AVATAR = `data:image/svg+xml;base64,${AI_AVATAR_BASE64}`;

export const welcomeMessage = (providers: string[]) => `
#### Ask JupyterLite AI
export const welcomeMessage = (
providers: string[],
trans: TranslationBundle
) => `
${trans.__('#### Ask JupyterLite AI')}


The provider to use can be set in the <button data-commandLinker-command="settingeditor:open" data-commandLinker-args='{"query": "AI provider"}' href="#">settings editor</button>, by selecting it from
the <img src="${AI_AVATAR}" width="16" height="16"> _AI provider_ settings.
${trans.__('The provider to use can be set in the')} <button data-commandLinker-command="settingeditor:open" data-commandLinker-args='{"query": "AI provider"}' href="#">${trans.__('settings editor')}</button>, ${trans.__('by selecting it from the')}
<img src="${AI_AVATAR}" width="16" height="16"> _${trans.__('AI provider')}_ ${trans.__('settings')}.

The current providers that are available are _${providers.sort().join('_, _')}_.
${trans.__('The current providers that are available are')} _${providers.sort().join('_, _')}_.

To clear the chat, you can use the \`/clear\` command from the chat input.
${trans.__('To clear the chat, you can use the')} \`${trans.__('/clear')}\` ${trans.__('command from the chat input')}.
`;

export type ConnectionMessage = {
Expand All @@ -56,6 +60,7 @@ export class ChatHandler extends AbstractChatModel {
constructor(options: ChatHandler.IOptions) {
super(options);
this._providerRegistry = options.providerRegistry;
this._translator = options.translator;

this._providerRegistry.providerChanged.connect(() => {
this._errorMessage = this._providerRegistry.chatError;
Expand Down Expand Up @@ -94,7 +99,7 @@ export class ChatHandler extends AbstractChatModel {

async sendMessage(message: INewMessage): Promise<boolean> {
const body = message.body;
if (body.startsWith('/clear')) {
if (body.startsWith(this._translator.__('/clear'))) {
// TODO: do we need a clear method?
this.messagesDeleted(0, this.messages.length);
this._history.messages = [];
Expand Down Expand Up @@ -201,8 +206,12 @@ export class ChatHandler extends AbstractChatModel {
private _personaName = 'AI';
private _errorMessage: string = '';
private _history: IChatHistory = { messages: [] };
private _defaultErrorMessage = 'AI provider not configured';
private _translator: TranslationBundle;
private _controller: AbortController | null = null;

get _defaultErrorMessage(): string {
return this._translator.__('AI provider not configured');
}
}

export namespace ChatHandler {
Expand All @@ -211,6 +220,7 @@ export namespace ChatHandler {
*/
export interface IOptions extends IChatModel.IOptions {
providerRegistry: IAIProviderRegistry;
translator: TranslationBundle;
}

/**
Expand All @@ -225,14 +235,22 @@ export namespace ChatHandler {
*/
export class ClearCommandProvider implements IChatCommandProvider {
public id: string = '@jupyterlite/ai:clear-commands';
private _slash_commands: ChatCommand[] = [
{
name: '/clear',
providerId: this.id,
replaceWith: '/clear',
description: 'Clear the chat'
}
];
private _translator: TranslationBundle;

constructor(translator: TranslationBundle) {
this._translator = translator;
}

private get _slash_commands(): ChatCommand[] {
return [
{
name: this._translator.__('/clear'),
providerId: this.id,
replaceWith: this._translator.__('/clear'),
description: this._translator.__('Clear the chat')
}
];
}
async listCommandCompletions(inputModel: IInputModel) {
const match = inputModel.currentWord?.match(/^\/\w*/)?.[0];
if (!match) {
Expand Down
12 changes: 9 additions & 3 deletions src/components/stop-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import StopIcon from '@mui/icons-material/Stop';
import React from 'react';

import { InputToolbarRegistry, TooltippedButton } from '@jupyter/chat';
import { TranslationBundle } from '@jupyterlab/translation';

/**
* Properties of the stop button.
Expand All @@ -17,13 +18,17 @@ export interface IStopButtonProps
* The function to stop streaming.
*/
stopStreaming: () => void;
/**
* The translation bundle.
*/
trans: TranslationBundle;
}

/**
* The stop button.
*/
export function StopButton(props: IStopButtonProps): JSX.Element {
const tooltip = 'Stop streaming';
const tooltip = props.trans.__('Stop streaming');
return (
<TooltippedButton
onClick={props.stopStreaming}
Expand All @@ -43,11 +48,12 @@ export function StopButton(props: IStopButtonProps): JSX.Element {
* factory returning the toolbar item.
*/
export function stopItem(
stopStreaming: () => void
stopStreaming: () => void,
trans: TranslationBundle
): InputToolbarRegistry.IToolbarItem {
return {
element: (props: InputToolbarRegistry.IToolbarItemProps) => {
const stopProps: IStopButtonProps = { ...props, stopStreaming };
const stopProps: IStopButtonProps = { ...props, stopStreaming, trans };
return StopButton(stopProps);
},
position: 50,
Expand Down
Loading