Skip to content
This repository was archived by the owner on Aug 30, 2025. It is now read-only.

Commit ba83f0b

Browse files
Copilotgodetremy
andcommitted
Implement gentle French language preferences feature
Co-authored-by: godetremy <[email protected]>
1 parent 05c4a29 commit ba83f0b

File tree

6 files changed

+316
-1
lines changed

6 files changed

+316
-1
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# French Language Preferences Feature
2+
3+
This feature provides gentle educational suggestions for French language usage within the Papillon app.
4+
5+
## Purpose
6+
7+
The feature promotes consistent and idiomatic French expressions, helping users improve their French while using the app. It's designed to be educational and helpful, not punitive.
8+
9+
## How it works
10+
11+
### 1. Text Analysis
12+
The system analyzes text in chat messages and discussions for common French expressions that could be improved.
13+
14+
### 2. Gentle Suggestions
15+
When certain expressions are detected (like "ça fonctionne" vs "ça marche"), the system logs gentle suggestions to help users learn more idiomatic French.
16+
17+
### 3. Educational Focus
18+
- **No blocking**: Users can still send messages with any expressions
19+
- **No banning**: This is purely educational, not punitive
20+
- **Logging only**: Suggestions are logged for educational purposes
21+
- **Respectful**: Promotes good French while respecting user choice
22+
23+
## Supported Expressions
24+
25+
Currently supports suggestions for:
26+
- "ça fonctionne" → "ça marche" (more idiomatic)
27+
- "fonctionne correctement" → "marche correctement" (more natural)
28+
29+
## Implementation
30+
31+
### Files
32+
- `src/utils/language/french-preferences.ts` - Core utility functions
33+
- `src/utils/language/french-education.ts` - Educational helper functions
34+
- `src/services/chats.ts` - Integration with chat services
35+
36+
### Usage
37+
```typescript
38+
import { checkFrenchPreferences, getFrenchSuggestionMessage } from "@/utils/language/french-preferences";
39+
40+
const text = "Ça fonctionne bien !";
41+
const suggestions = checkFrenchPreferences(text);
42+
if (suggestions.length > 0) {
43+
const message = getFrenchSuggestionMessage(suggestions);
44+
console.log(message); // "💡 Suggestion: "ça marche" est plus idiomatique que "ça fonctionne""
45+
}
46+
```
47+
48+
## Testing
49+
50+
Run the test in developer menu:
51+
1. Go to DevMenu
52+
2. Tap "Test des suggestions françaises"
53+
3. Receive a notification with a language suggestion example
54+
55+
## Philosophy
56+
57+
This feature embodies the spirit of constructive language education - helping users improve their French through gentle, respectful suggestions while maintaining full functionality and user freedom.

src/services/chats.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Recipient } from "./shared/Recipient";
44
import {getFeatureAccount} from "@/utils/multiservice";
55
import {MultiServiceFeature} from "@/stores/multiService/types";
66
import {log} from "@/utils/logger/logger";
7+
import { checkFrenchPreferences, getFrenchSuggestionMessage } from "@/utils/language/french-preferences";
78

89
export const getChats = async <T extends Account> (account: T): Promise<Array<Chat>> => {
910
switch (account.service) {
@@ -63,6 +64,13 @@ export const getChatRecipients = async <T extends Account> (account: T, chat: Ch
6364
};
6465

6566
export const sendMessageInChat = async <T extends Account> (account: T, chat: Chat, content: string): Promise<void> => {
67+
// Gentle educational feature: check for French language preferences
68+
const suggestions = checkFrenchPreferences(content);
69+
if (suggestions.length > 0) {
70+
const suggestionMsg = getFrenchSuggestionMessage(suggestions);
71+
log(`💡 Language suggestion: ${suggestionMsg}`, "language-preferences");
72+
}
73+
6674
switch (account.service) {
6775
case AccountService.Pronote: {
6876
const { sendMessageInChat } = await import("./pronote/chats");
@@ -129,6 +137,20 @@ export const createDiscussionRecipients = async <T extends Account> (account: T)
129137
};
130138

131139
export const createDiscussion = async <T extends Account> (account: T, subject: string, content: string, recipients: Recipient[]): Promise<void> => {
140+
// Gentle educational feature: check for French language preferences in both subject and content
141+
const subjectSuggestions = checkFrenchPreferences(subject);
142+
const contentSuggestions = checkFrenchPreferences(content);
143+
144+
if (subjectSuggestions.length > 0) {
145+
const suggestionMsg = getFrenchSuggestionMessage(subjectSuggestions);
146+
log(`💡 Language suggestion for subject: ${suggestionMsg}`, "language-preferences");
147+
}
148+
149+
if (contentSuggestions.length > 0) {
150+
const suggestionMsg = getFrenchSuggestionMessage(contentSuggestions);
151+
log(`💡 Language suggestion for content: ${suggestionMsg}`, "language-preferences");
152+
}
153+
132154
switch (account.service) {
133155
case AccountService.Pronote: {
134156
const { createDiscussion } = await import("./pronote/chats");
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Educational French Language Helper
3+
*
4+
* This module provides gentle educational features to help users improve their French.
5+
* It's designed to be helpful and educational, not punitive.
6+
*/
7+
8+
import { checkFrenchPreferences, getFrenchSuggestionMessage } from "./french-preferences";
9+
import { log } from "@/utils/logger/logger";
10+
11+
/**
12+
* Educational logger that provides gentle French suggestions
13+
*/
14+
export const logFrenchSuggestion = (text: string, context: string = "text") => {
15+
const suggestions = checkFrenchPreferences(text);
16+
17+
if (suggestions.length > 0) {
18+
const message = getFrenchSuggestionMessage(suggestions);
19+
log(`📚 ${message} (contexte: ${context})`, "french-education");
20+
return suggestions;
21+
}
22+
23+
return [];
24+
};
25+
26+
/**
27+
* Check if user is using preferred French expressions
28+
* Returns true if the text uses good French, false if suggestions are available
29+
*/
30+
export const isGoodFrench = (text: string): boolean => {
31+
const suggestions = checkFrenchPreferences(text);
32+
return suggestions.length === 0;
33+
};
34+
35+
/**
36+
* Educational mode: returns the text with gentle improvements if any
37+
*/
38+
export const educationalMode = (text: string, enableSuggestions: boolean = true): {
39+
improved: string;
40+
hasSuggestions: boolean;
41+
suggestions: string[];
42+
} => {
43+
const suggestions = checkFrenchPreferences(text);
44+
45+
if (enableSuggestions && suggestions.length > 0) {
46+
logFrenchSuggestion(text, "educational-mode");
47+
}
48+
49+
return {
50+
improved: text, // Don't auto-correct, just suggest
51+
hasSuggestions: suggestions.length > 0,
52+
suggestions: suggestions.map(s => getFrenchSuggestionMessage([s]))
53+
};
54+
};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Tests for French Language Preferences Utility
3+
*/
4+
5+
import {
6+
checkFrenchPreferences,
7+
applyFrenchPreferences,
8+
getFrenchSuggestionMessage,
9+
FRENCH_PREFERENCES
10+
} from '../french-preferences';
11+
12+
// Simple test function since this repo doesn't have a formal test framework
13+
export const runFrenchPreferencesTests = () => {
14+
console.log('🧪 Running French Preferences Tests...');
15+
16+
// Test 1: Check detection of "ça fonctionne"
17+
const text1 = "Bonjour, ça fonctionne très bien maintenant!";
18+
const suggestions1 = checkFrenchPreferences(text1);
19+
console.assert(suggestions1.length > 0, "Should detect 'ça fonctionne'");
20+
console.assert(suggestions1[0].preferred === "ça marche", "Should suggest 'ça marche'");
21+
22+
// Test 2: Apply preferences
23+
const improved1 = applyFrenchPreferences(text1);
24+
console.assert(improved1.includes("ça marche"), "Should replace with 'ça marche'");
25+
console.assert(!improved1.includes("ça fonctionne"), "Should not contain 'ça fonctionne'");
26+
27+
// Test 3: Test with text that doesn't need changes
28+
const text2 = "Tout marche parfaitement!";
29+
const suggestions2 = checkFrenchPreferences(text2);
30+
console.assert(suggestions2.length === 0, "Should not suggest changes for good text");
31+
32+
// Test 4: Test suggestion message generation
33+
const message = getFrenchSuggestionMessage(suggestions1);
34+
console.assert(message.includes("💡"), "Should include suggestion emoji");
35+
console.assert(message.includes("ça marche"), "Should mention preferred expression");
36+
37+
// Test 5: Case insensitive detection
38+
const text3 = "ÇA FONCTIONNE BIEN";
39+
const suggestions3 = checkFrenchPreferences(text3);
40+
console.assert(suggestions3.length > 0, "Should detect uppercase text");
41+
42+
console.log('✅ All French Preferences tests passed!');
43+
44+
// Log some examples
45+
console.log('\n📝 Examples:');
46+
console.log('Original:', text1);
47+
console.log('Improved:', improved1);
48+
console.log('Suggestion:', message);
49+
};
50+
51+
// Run tests if this file is executed directly
52+
if (typeof window === 'undefined' && typeof global !== 'undefined') {
53+
runFrenchPreferencesTests();
54+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* French Language Preferences Utility
3+
*
4+
* This utility helps maintain consistent and preferred French expressions
5+
* throughout the Papillon app, promoting proper French language usage.
6+
*/
7+
8+
export interface FrenchTextSuggestion {
9+
original: string;
10+
preferred: string;
11+
reason?: string;
12+
}
13+
14+
/**
15+
* Common French expression preferences
16+
*/
17+
export const FRENCH_PREFERENCES: FrenchTextSuggestion[] = [
18+
{
19+
original: "ça fonctionne",
20+
preferred: "ça marche",
21+
reason: "Plus idiomatique en français courant"
22+
},
23+
{
24+
original: "fonctionne correctement",
25+
preferred: "marche correctement",
26+
reason: "Expression plus naturelle"
27+
},
28+
{
29+
original: "fonctionnement",
30+
preferred: "fonctionnement", // This one is actually fine as a noun
31+
reason: "Acceptable en tant que nom"
32+
}
33+
];
34+
35+
/**
36+
* Checks if text contains expressions that could be improved
37+
*/
38+
export const checkFrenchPreferences = (text: string): FrenchTextSuggestion[] => {
39+
const suggestions: FrenchTextSuggestion[] = [];
40+
41+
for (const preference of FRENCH_PREFERENCES) {
42+
const lowerText = text.toLowerCase();
43+
const lowerOriginal = preference.original.toLowerCase();
44+
45+
if (lowerText.indexOf(lowerOriginal) !== -1 &&
46+
preference.original !== preference.preferred) {
47+
suggestions.push(preference);
48+
}
49+
}
50+
51+
return suggestions;
52+
};
53+
54+
/**
55+
* Applies preferred French expressions to text
56+
*/
57+
export const applyFrenchPreferences = (text: string): string => {
58+
let improvedText = text;
59+
60+
for (const preference of FRENCH_PREFERENCES) {
61+
if (preference.original !== preference.preferred) {
62+
// Replace case-insensitive
63+
const regex = new RegExp(preference.original, 'gi');
64+
improvedText = improvedText.replace(regex, preference.preferred);
65+
}
66+
}
67+
68+
return improvedText;
69+
};
70+
71+
/**
72+
* Get a friendly suggestion message for better French
73+
*/
74+
export const getFrenchSuggestionMessage = (suggestions: FrenchTextSuggestion[]): string => {
75+
if (suggestions.length === 0) return "";
76+
77+
const suggestion = suggestions[0];
78+
return `💡 Suggestion: "${suggestion.preferred}" est plus idiomatique que "${suggestion.original}"`;
79+
};

src/views/welcome/DevMenu.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,63 @@ const DevMenu: Screen<"DevMenu"> = ({ navigation }) => {
282282
id: "test",
283283
title: "Coucou, c'est Papillon 👋",
284284
subtitle: "Test",
285-
body: "Si tu me vois, c'est que tout fonctionne correctement !",
285+
body: "Si tu me vois, c'est que tout marche correctement !",
286286
},
287287
"Test"
288288
);
289289
setTimeout(() => {
290290
setLoading(false);
291291
}, 500);
292292
}}
293+
/>
294+
<NativeItem
295+
title={"Test des suggestions françaises"}
296+
trailing={
297+
loading ? (
298+
<View>
299+
<PapillonSpinner
300+
strokeWidth={3}
301+
size={22}
302+
color={theme.colors.text}
303+
/>
304+
</View>
305+
) : undefined
306+
}
307+
disabled={loading}
308+
onPress={async () => {
309+
setLoading(true);
310+
// Import the French preferences utility
311+
const { checkFrenchPreferences, getFrenchSuggestionMessage } = await import("@/utils/language/french-preferences");
312+
313+
const testText = "Ça fonctionne très bien cette fonctionnalité !";
314+
const suggestions = checkFrenchPreferences(testText);
315+
316+
if (suggestions.length > 0) {
317+
const suggestionMsg = getFrenchSuggestionMessage(suggestions);
318+
await papillonNotify(
319+
{
320+
id: "french-suggestion",
321+
title: "Suggestion linguistique 🇫🇷",
322+
subtitle: "Amélioration du français",
323+
body: suggestionMsg,
324+
},
325+
"Language"
326+
);
327+
} else {
328+
await papillonNotify(
329+
{
330+
id: "french-good",
331+
title: "Excellent français ! 👌",
332+
subtitle: "Langue parfaite",
333+
body: "Aucune suggestion nécessaire, votre français est impeccable !",
334+
},
335+
"Language"
336+
);
337+
}
338+
setTimeout(() => {
339+
setLoading(false);
340+
}, 500);
341+
}}
293342
/></NativeList>
294343
</View>
295344
)}

0 commit comments

Comments
 (0)