Ajouter les premières langues africaines (traduction) #6

Merged
cedric merged 1 commits from feat/africa into master 2026-07-05 22:08:54 +00:00
4 changed files with 99 additions and 2 deletions
@@ -57,4 +57,28 @@ describe('translateLyrics', () => {
expect(result.anglais).toContain('traduit-EN'); expect(result.anglais).toContain('traduit-EN');
expect(result.espagnol).toBeUndefined(); expect(result.espagnol).toBeUndefined();
}); });
it('traduit les langues africaines ajoutées, sans appeler DeepL pour le yoruba', async () => {
global.fetch = vi.fn(async (_url, options) => {
const {target_lang: target} = JSON.parse(options.body);
return fakeDeeplResponse(`traduit-${target}`);
});
const strapi = {
contentType: vi.fn(() => ({uid: 'api::parole.parole', kind: 'collectionType'})),
log: {error: vi.fn()}
};
const service = createService({strapi});
const result = await service.translateLyrics('Bonjour le monde');
expect(result.swahili).toContain('traduit-SW');
expect(result.lingala).toContain('traduit-LN');
expect(result.wolof).toContain('traduit-WO');
expect(result.hausa).toContain('traduit-HA');
expect(result.yoruba).toBeUndefined();
const calledTargets = global.fetch.mock.calls.map(([, options]) => JSON.parse(options.body).target_lang);
expect(calledTargets).not.toContain('YO');
expect(calledTargets).not.toContain(undefined);
});
}); });
+19 -2
View File
@@ -6,6 +6,21 @@ const { ApplicationError } = require('@strapi/utils').errors;
const LANG_MAP = { const LANG_MAP = {
fr: { field: 'francais', targetLang: 'fr', userPrompt: 'Tradui an fransé' }, fr: { field: 'francais', targetLang: 'fr', userPrompt: 'Tradui an fransé' },
// Pas de code DeepL pour le yoruba : traduction manuelle uniquement.
yo: { field: 'yoruba', targetLang: 'yo', userPrompt: 'Translate to Yoruba' },
ln: { field: 'lingala', targetLang: 'ln', userPrompt: 'Translate to Lingala', deeplTarget: 'LN', suffix: '\n\n (DeepL)' },
wo: { field: 'wolof', targetLang: 'wo', userPrompt: 'Translate to Wolof', deeplTarget: 'WO', suffix: '\n\n (DeepL)' },
sw: { field: 'swahili', targetLang: 'sw', userPrompt: 'Translate to Swahili', deeplTarget: 'SW', suffix: '\n\n (DeepL)' },
ha: { field: 'hausa', targetLang: 'ha', userPrompt: 'Translate to Hausa', deeplTarget: 'HA', suffix: '\n\n (DeepL)' },
ar: { field: 'arabe', targetLang: 'ar', userPrompt: 'Translate to Arabic', deeplTarget: 'AR', suffix: '\n\n (DeepL)' },
om: { field: 'oromo', targetLang: 'om', userPrompt: 'Translate to Oromo', deeplTarget: 'OM', suffix: '\n\n (DeepL)' },
ig: { field: 'igbo', targetLang: 'ig', userPrompt: 'Translate to Igbo', deeplTarget: 'IG', suffix: '\n\n (DeepL)' },
zu: { field: 'zoulou', targetLang: 'zu', userPrompt: 'Translate to Zulu', deeplTarget: 'ZU', suffix: '\n\n (DeepL)' },
mg: { field: 'malgache', targetLang: 'mg', userPrompt: 'Translate to Malagasy', deeplTarget: 'MG', suffix: '\n\n (DeepL)' },
xh: { field: 'xhosa', targetLang: 'xh', userPrompt: 'Translate to Xhosa', deeplTarget: 'XH', suffix: '\n\n (DeepL)' },
tn: { field: 'tswana', targetLang: 'tn', userPrompt: 'Translate to Tswana', deeplTarget: 'TN', suffix: '\n\n (DeepL)' },
ts: { field: 'tsonga', targetLang: 'ts', userPrompt: 'Translate to Tsonga', deeplTarget: 'TS', suffix: '\n\n (DeepL)' },
st: { field: 'sesotho', targetLang: 'st', userPrompt: 'Translate to Sesotho', deeplTarget: 'ST', suffix: '\n\n (DeepL)' },
ja: { field: 'japonais', targetLang: 'ja', userPrompt: '日本語に翻訳して', deeplTarget: 'JA', suffix: '\n\n (DeepLによる翻訳)' }, ja: { field: 'japonais', targetLang: 'ja', userPrompt: '日本語に翻訳して', deeplTarget: 'JA', suffix: '\n\n (DeepLによる翻訳)' },
ko: { field: 'coreen', targetLang: 'ko', userPrompt: '한국어로 번역해줘', deeplTarget: 'KO', suffix: '\n\n (DeepL 번역)' }, ko: { field: 'coreen', targetLang: 'ko', userPrompt: '한국어로 번역해줘', deeplTarget: 'KO', suffix: '\n\n (DeepL 번역)' },
en: { field: 'anglais', targetLang: 'en', userPrompt: 'Translate to English', deeplTarget: 'EN', suffix: '\n\n (Translated by DeepL)' }, en: { field: 'anglais', targetLang: 'en', userPrompt: 'Translate to English', deeplTarget: 'EN', suffix: '\n\n (Translated by DeepL)' },
@@ -16,6 +31,8 @@ const LANG_MAP = {
}; };
const ALL_LANGS = Object.keys(LANG_MAP); const ALL_LANGS = Object.keys(LANG_MAP);
// Langues suivies (schéma + export) mais sans traduction automatique DeepL.
const NO_AUTO_TRANSLATE = new Set(['fr', 'yo']);
function stripMarkdown(text) { function stripMarkdown(text) {
if (!text) return ''; if (!text) return '';
@@ -88,7 +105,7 @@ module.exports = createCoreService('api::parole.parole', ({strapi}) => ({
const result = { francais: parolesFR }; const result = { francais: parolesFR };
for (const lang of ALL_LANGS) { for (const lang of ALL_LANGS) {
if (lang === 'fr') continue; if (NO_AUTO_TRANSLATE.has(lang)) continue;
const { field, deeplTarget, suffix } = LANG_MAP[lang]; const { field, deeplTarget, suffix } = LANG_MAP[lang];
try { try {
const translated = await this.translate('FR', deeplTarget, parolesFR); const translated = await this.translate('FR', deeplTarget, parolesFR);
@@ -203,7 +220,7 @@ module.exports = createCoreService('api::parole.parole', ({strapi}) => ({
async bulkTranslateMissing() { async bulkTranslateMissing() {
const TARGET_LANGS = ALL_LANGS const TARGET_LANGS = ALL_LANGS
.filter(lang => lang !== 'fr') .filter(lang => !NO_AUTO_TRANSLATE.has(lang))
.map(lang => ({ lang, ...LANG_MAP[lang] })); .map(lang => ({ lang, ...LANG_MAP[lang] }));
const pageSize = 100; const pageSize = 100;
+42
View File
@@ -10,6 +10,48 @@
"francais": { "francais": {
"type": "richtext" "type": "richtext"
}, },
"yoruba": {
"type": "richtext"
},
"lingala": {
"type": "richtext"
},
"wolof": {
"type": "richtext"
},
"swahili": {
"type": "richtext"
},
"hausa": {
"type": "richtext"
},
"arabe": {
"type": "richtext"
},
"oromo": {
"type": "richtext"
},
"igbo": {
"type": "richtext"
},
"zoulou": {
"type": "richtext"
},
"malgache": {
"type": "richtext"
},
"xhosa": {
"type": "richtext"
},
"tswana": {
"type": "richtext"
},
"tsonga": {
"type": "richtext"
},
"sesotho": {
"type": "richtext"
},
"japonais": { "japonais": {
"type": "richtext" "type": "richtext"
}, },
+14
View File
@@ -109,12 +109,26 @@ export interface TradTraductions extends Struct.ComponentSchema {
attributes: { attributes: {
allemand: Schema.Attribute.RichText; allemand: Schema.Attribute.RichText;
anglais: Schema.Attribute.RichText; anglais: Schema.Attribute.RichText;
arabe: Schema.Attribute.RichText;
coreen: Schema.Attribute.RichText; coreen: Schema.Attribute.RichText;
espagnol: Schema.Attribute.RichText; espagnol: Schema.Attribute.RichText;
francais: Schema.Attribute.RichText; francais: Schema.Attribute.RichText;
hausa: Schema.Attribute.RichText;
igbo: Schema.Attribute.RichText;
italien: Schema.Attribute.RichText; italien: Schema.Attribute.RichText;
japonais: Schema.Attribute.RichText; japonais: Schema.Attribute.RichText;
lingala: Schema.Attribute.RichText;
malgache: Schema.Attribute.RichText;
oromo: Schema.Attribute.RichText;
portugais: Schema.Attribute.RichText; portugais: Schema.Attribute.RichText;
sesotho: Schema.Attribute.RichText;
swahili: Schema.Attribute.RichText;
tsonga: Schema.Attribute.RichText;
tswana: Schema.Attribute.RichText;
wolof: Schema.Attribute.RichText;
xhosa: Schema.Attribute.RichText;
yoruba: Schema.Attribute.RichText;
zoulou: Schema.Attribute.RichText;
}; };
} }