mirror of
https://github.com/storytold/craft-launcher.git
synced 2026-10-14 14:26:48 -04:00
Add a French interface (#85)
* Add a French interface fr.tsv translates the whole interface, the tray menu and tooltip, each app's tagline and the backend's install/update messages, and Settings > Language offers Français. fr, fr-FR, fr_CA.UTF-8 and the like select it. French counts 0 and 1 as singular, so it gets its own plural rule. tests/french.rs checks the grid, the table, dates and a backend message. * Rebase onto German: French last in the registry, new update strings, lowercase months French writes month abbreviations in lower case (12 août 2026). Tests use a never-supported tag (xx-XX) next to fr-FR. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
5 files changed
+489
-6
No files matched your search
@@ -84,10 +84,10 @@ The web version shows the same catalog in the browser, with the latest download
|
||||
|
||||
## Interface languages
|
||||
|
||||
The interface is in English, German (Deutsch), Russian (Русский) or Simplified Chinese
|
||||
The interface is in English, French (Français), German (Deutsch), Russian (Русский) or Simplified Chinese
|
||||
(简体中文). **Auto**, the default, follows the system or browser language; **Settings → Language**
|
||||
picks one and switches at once, and the choice is kept between runs. German locales (`de-DE`,
|
||||
`de-AT`, `de-CH`, `de_DE.UTF-8`…) select German. Simplified Chinese locales (`zh-CN`, `zh-SG`,
|
||||
`de-AT`, `de-CH`, `de_DE.UTF-8`…) select German, and French ones (`fr-FR`, `fr-CA`…) French. Simplified Chinese locales (`zh-CN`, `zh-SG`,
|
||||
`zh-Hans`, and POSIX forms such as `zh_CN.UTF-8`) select Chinese; Traditional Chinese ones don't.
|
||||
To try a language without changing the setting, start the desktop app with
|
||||
`ARTCRAFT_LAUNCHER_LOCALE=zh-CN`, or add `?lang=ru` to the web address.
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
# French (fr) catalog for ArtCraft Launcher.
|
||||
# Format: context<TAB>source<TAB>translation
|
||||
# Empty context = plain UI string (tl!, t).
|
||||
# @id = command-id keyed (tr_id).
|
||||
# @plural = plural forms: one|other (0 and 1 are "one" in French).
|
||||
# @msg = status/error message template (message, msg).
|
||||
# Other context = disambiguation for tr_ctx.
|
||||
#
|
||||
# Clean-room: written from the meaning of ArtCraft Launcher's English labels, in neutral
|
||||
# international French (the interface addresses the user as "vous").
|
||||
#
|
||||
# Coverage: the whole interface: every menu string and tl! literal (complete_menus = true).
|
||||
# Left in English on purpose: product name "ArtCraft Launcher" (and its "Launcher" tag), suite
|
||||
# name "Craft App Suite", app names (PhotoCraft, LightCraft, FilmCraft, DesignCraft, VectorCraft,
|
||||
# PdfCraft, EffectCraft, WordCraft, DeckCraft, GridCraft, CADCraft, SoundCraft), "GitHub",
|
||||
# "Discord".
|
||||
# Trailing "…" (U+2026) must be preserved. Placeholders {name}, {n}, {version}, {count}, {v},
|
||||
# {what}, {unit}, {real}, {e} must appear in translation.
|
||||
|
||||
Launcher Launcher
|
||||
Craft App Suite Craft App Suite
|
||||
Open Source Tools for Creatives, Makers, and Builders Des outils open source pour les créatifs, les makers et les bâtisseurs
|
||||
Settings Réglages
|
||||
Check for updates Rechercher des mises à jour
|
||||
Grid Grille
|
||||
Table Tableau
|
||||
Show apps as cards Afficher les applications en cartes
|
||||
Show apps as a table Afficher les applications en tableau
|
||||
All Toutes
|
||||
Updates Mises à jour
|
||||
Not installed Non installées
|
||||
Search apps Rechercher des applications
|
||||
Sort Tri
|
||||
Suite order Ordre de la suite
|
||||
Name Nom
|
||||
Inspired by Inspirée de
|
||||
Status État
|
||||
Installed version Version installée
|
||||
Latest version Dernière version
|
||||
Newest release Version la plus récente
|
||||
Discord Discord
|
||||
GitHub GitHub
|
||||
Website Site web
|
||||
Classic apps Applications classiques
|
||||
New apps Nouvelles applications
|
||||
NEW NOUVEAU
|
||||
Install Installer
|
||||
Update to {v} Mettre à jour vers {v}
|
||||
Open Ouvrir
|
||||
Download for {os} Télécharger pour {os}
|
||||
Download Télécharger
|
||||
Coming soon Bientôt disponible
|
||||
Checking… Vérification…
|
||||
Not available Indisponible
|
||||
Offline Hors ligne
|
||||
Downloading {done} of {total} Téléchargement de {done} sur {total}
|
||||
Downloading… Téléchargement…
|
||||
Reconnecting… Reconnexion…
|
||||
Verifying… Vérification…
|
||||
Installing, then restarting… Installation, puis redémarrage…
|
||||
Installing… Installation…
|
||||
Removing… Suppression…
|
||||
Pause download Mettre le téléchargement en pause
|
||||
Cancel download Annuler le téléchargement
|
||||
Resume download Reprendre le téléchargement
|
||||
Discard download Supprimer le téléchargement
|
||||
Release notes Notes de version
|
||||
Show in Finder Afficher dans le Finder
|
||||
Show in Explorer Afficher dans l'Explorateur
|
||||
Show in Files Afficher dans Fichiers
|
||||
Reinstall Réinstaller
|
||||
Uninstall… Désinstaller…
|
||||
All releases Toutes les versions
|
||||
ArtCraft Launcher {version} is available. ArtCraft Launcher {version} est disponible.
|
||||
Dismiss Ignorer
|
||||
Update and restart Mettre à jour et redémarrer
|
||||
A new version is available. Would you like to relaunch with the updated version? Une nouvelle version est disponible. Redémarrer avec la nouvelle version ?
|
||||
Later Plus tard
|
||||
What's new Nouveautés
|
||||
No notes were published with this release. Aucune note n'a été publiée avec cette version.
|
||||
Released {date} Publiée le {date}
|
||||
Open on GitHub Ouvrir sur GitHub
|
||||
Close Fermer
|
||||
ArtCraft Launcher {version} ArtCraft Launcher {version}
|
||||
Only when I ask Seulement à ma demande
|
||||
Automatically download and install updates Télécharger et installer les mises à jour automatiquement
|
||||
Notify me when an installed app has an update Me prévenir quand une application installée a une mise à jour
|
||||
Include pre-releases (betas and release candidates) Inclure les préversions (bêtas et versions candidates)
|
||||
Installing on Linux Installation sous Linux
|
||||
Install new apps as Installer les nouvelles applications en tant que
|
||||
System package (recommended) Paquet système (recommandé)
|
||||
Updates always use the format an app was installed with. Les mises à jour utilisent toujours le format avec lequel l'application a été installée.
|
||||
Running in the background Exécution en arrière-plan
|
||||
Keep running in the tray when the window is closed Rester actif dans la zone de notification à la fermeture de la fenêtre
|
||||
Show the window on the current desktop Afficher la fenêtre sur le bureau actuel
|
||||
Start ArtCraft Launcher when I log in Lancer ArtCraft Launcher à l'ouverture de session
|
||||
Community Communauté
|
||||
Questions, ideas and news from the ArtCraft team Questions, idées et actualités de l'équipe ArtCraft
|
||||
Appearance Apparence
|
||||
Quit ArtCraft Launcher Quitter ArtCraft Launcher
|
||||
Done Terminé
|
||||
Uninstall {name}? Désinstaller {name} ?
|
||||
The app will be moved to the Trash. Your documents and settings stay. L'application sera placée dans la Corbeille. Vos documents et réglages sont conservés.
|
||||
Windows will remove it like any installed program. Your documents and settings stay. Windows la supprimera comme n'importe quel programme installé. Vos documents et réglages sont conservés.
|
||||
Your system's package manager will remove it, and may ask for your password. Your documents and settings stay. Le gestionnaire de paquets de votre système la supprimera et pourra vous demander votre mot de passe. Vos documents et réglages sont conservés.
|
||||
The app will be removed. Your documents and settings stay. L'application sera supprimée. Vos documents et réglages sont conservés.
|
||||
Uninstall Désinstaller
|
||||
Cancel Annuler
|
||||
Checked {ago} Vérifié {ago}
|
||||
just now à l'instant
|
||||
yesterday hier
|
||||
{month} {day}, {year} {day} {month} {year}
|
||||
|
||||
Automatically Automatiquement
|
||||
Every Craft app Toutes les applications Craft
|
||||
Apps on this computer Applications sur cet ordinateur
|
||||
Installed apps with a newer version Applications installées avec une version plus récente
|
||||
Apps you can install Applications que vous pouvez installer
|
||||
Checked {ago} · using {sources} Vérifié {ago} · via {sources}
|
||||
{a} and {b} {a} et {b}
|
||||
Join the Discord Rejoindre le Discord
|
||||
Learn more En savoir plus
|
||||
Minimize Réduire
|
||||
Maximize Agrandir
|
||||
Restore Restaurer
|
||||
New Nouveau
|
||||
OK OK
|
||||
Open link Ouvrir le lien
|
||||
Update Mettre à jour
|
||||
{name} icon Icône de {name}
|
||||
Loading ArtCraft Launcher… Chargement d'ArtCraft Launcher…
|
||||
Cancel update Annuler la mise à jour
|
||||
Stop waiting; the update can be started again later Ne plus attendre ; la mise à jour pourra être relancée plus tard
|
||||
Download again Télécharger à nouveau
|
||||
Install cancelled Installation annulée
|
||||
Install cancelled. The download is kept for the next try. Installation annulée. Le téléchargement est conservé pour la prochaine tentative.
|
||||
Update cancelled Mise à jour annulée
|
||||
Uninstall cancelled Désinstallation annulée
|
||||
Retry install Réessayer l'installation
|
||||
Retry update to {version} Réessayer la mise à jour vers {version}
|
||||
The download is still here. It's checked again, then installed. Le téléchargement est toujours là. Il sera vérifié à nouveau, puis installé.
|
||||
Waiting for another app… En attente d'une autre application…
|
||||
Waiting for other installs… En attente d'autres installations…
|
||||
|
||||
# Package formats offered for download (PackageKind::label)
|
||||
Installer (.msi) Programme d'installation (.msi)
|
||||
Portable (.zip) Version portable (.zip)
|
||||
Disk image (.dmg) Image disque (.dmg)
|
||||
Debian package (.deb) Paquet Debian (.deb)
|
||||
RPM package (.rpm) Paquet RPM (.rpm)
|
||||
Flatpak bundle Paquet Flatpak
|
||||
Tarball (.tar.gz) Archive (.tar.gz)
|
||||
|
||||
# Contextual: each app's tagline, keyed by app id
|
||||
photocraft Image editing Retouche d'images
|
||||
vectorcraft Vector illustration Illustration vectorielle
|
||||
filmcraft Video editing, color and sound Montage vidéo, étalonnage et son
|
||||
lightcraft Photo library and raw development Photothèque et développement RAW
|
||||
pdfcraft Reading, organizing and protecting PDFs Lecture, organisation et protection de PDF
|
||||
effectcraft Motion graphics and visual effects Motion design et effets visuels
|
||||
designcraft Page layout and publishing Mise en page et édition
|
||||
wordcraft Writing and document design Rédaction et mise en forme de documents
|
||||
deckcraft Presentations and slide shows Présentations et diaporamas
|
||||
gridcraft Spreadsheets and data Tableurs et données
|
||||
cadcraft Computer-aided design and drafting Conception et dessin assistés par ordinateur
|
||||
soundcraft Recording, editing and mixing audio Enregistrement, édition et mixage audio
|
||||
|
||||
# Contextual: month abbreviations for date rendering
|
||||
month Jan janv.
|
||||
month Feb févr.
|
||||
month Mar mars
|
||||
month Apr avr.
|
||||
month May mai
|
||||
month Jun juin
|
||||
month Jul juil.
|
||||
month Aug août
|
||||
month Sep sept.
|
||||
month Oct oct.
|
||||
month Nov nov.
|
||||
month Dec déc.
|
||||
Not checked yet Pas encore vérifié
|
||||
GitHub's hourly limit was reached; full details return in {min} min La limite horaire de GitHub a été atteinte ; les détails complets reviennent dans {min} min
|
||||
Installing for {platform} Installation pour {platform}
|
||||
Downloads for {platform} Téléchargements pour {platform}
|
||||
No downloads for this system Aucun téléchargement pour ce système
|
||||
Join the ArtCraft community on Discord Rejoindre la communauté ArtCraft sur Discord
|
||||
ArtCraft on {name} ArtCraft sur {name}
|
||||
Couldn't open {name}: {e} Impossible d'ouvrir {name} : {e}
|
||||
Download cancelled Téléchargement annulé
|
||||
Available to install Disponible à l'installation
|
||||
Available Disponible
|
||||
Installed · up to date Installé · à jour
|
||||
Update available Mise à jour disponible
|
||||
Installed Installé
|
||||
installed installé
|
||||
Not available here Indisponible ici
|
||||
Updating… Mise à jour…
|
||||
Update paused Mise à jour en pause
|
||||
Download paused Téléchargement en pause
|
||||
{version} installed → {latest} released {version} installée → {latest} publiée
|
||||
Version {version} Version {version}
|
||||
{installed} installed · latest {latest} {installed} installée · dernière {latest}
|
||||
Version {version} installed Version {version} installée
|
||||
Version {latest} Version {latest}
|
||||
No release published yet Aucune version publiée pour l'instant
|
||||
ArtCraft Launcher ArtCraft Launcher
|
||||
App Application
|
||||
Latest Dernière
|
||||
Released Publiée
|
||||
Auto Auto
|
||||
Language Langue
|
||||
Ascending Croissant
|
||||
Descending Décroissant
|
||||
Click to reverse the order Cliquez pour inverser l'ordre
|
||||
Click to sort by this column Cliquez pour trier selon cette colonne
|
||||
Like {name} Comme {name}
|
||||
{done} of {total} {done} sur {total}
|
||||
Downloading Téléchargement
|
||||
Verifying checksum… Vérification de la somme de contrôle…
|
||||
Resume Reprendre
|
||||
{done} of {total} downloaded {done} sur {total} téléchargés
|
||||
{done} downloaded {done} téléchargés
|
||||
Checking for updates… Recherche de mises à jour…
|
||||
Downloads the new version, replaces this one and opens it again Télécharge la nouvelle version, remplace celle-ci et la rouvre
|
||||
Couldn't check for updates: {f} Impossible de rechercher les mises à jour : {f}
|
||||
Website (coming soon) Site web (bientôt disponible)
|
||||
GitHub repository Dépôt GitHub
|
||||
ArtCraft ArtCraft
|
||||
Update 1 app Mettre à jour 1 application
|
||||
Update all ({count}) Tout mettre à jour ({count})
|
||||
⊞ Grid ⊞ Grille
|
||||
☰ Table ☰ Tableau
|
||||
🔍 Search apps 🔍 Rechercher des applications
|
||||
{name} {version} {name} {version}
|
||||
Match system Comme le système
|
||||
Dark Sombre
|
||||
Light Clair
|
||||
Full changelog Journal des modifications complet
|
||||
link lien
|
||||
No apps match Aucune application ne correspond
|
||||
Open ArtCraft Launcher to update. Ouvrez ArtCraft Launcher pour mettre à jour.
|
||||
{name} {v} is available {name} {v} est disponible
|
||||
{count} updates are available {count} mises à jour sont disponibles
|
||||
These notes weren't fetched (GitHub's request limit was reached). They're on the release page. Ces notes n'ont pas pu être récupérées (la limite de requêtes de GitHub a été atteinte). Elles sont sur la page de la version.
|
||||
When on, opening the launcher from the menu bar brings its window to the desktop you're on. When off, macOS switches you to the desktop the window was on. Si cette option est activée, ouvrir le launcher depuis la barre des menus amène sa fenêtre sur le bureau où vous êtes. Sinon, macOS vous renvoie sur le bureau où se trouvait la fenêtre.
|
||||
Couldn't change the login item: {e} Impossible de modifier l'élément d'ouverture de session : {e}
|
||||
|
||||
# Contextual: table column headers
|
||||
column App Application
|
||||
column Installed Installée
|
||||
column Latest Dernière
|
||||
column Released Date de publication
|
||||
|
||||
# Contextual: the filter tab names a group (the status pill says "Installed")
|
||||
filter Installed Installées
|
||||
|
||||
# Plural forms for French (one|other)
|
||||
@plural {n} app|{n} apps {n} application|{n} applications
|
||||
@plural {n} min ago|{n} min ago il y a {n} min|il y a {n} min
|
||||
@plural {n} h ago|{n} h ago il y a {n} h|il y a {n} h
|
||||
@plural {n} day ago|{n} days ago il y a {n} jour|il y a {n} jours
|
||||
|
||||
# Message templates (@msg): toasts from the backend. {placeholders} match any text.
|
||||
@msg Couldn't open {name}: {e} Impossible d'ouvrir {name} : {e}
|
||||
@msg Download cancelled Téléchargement annulé
|
||||
@msg {name} updated to {version} {name} mis à jour vers {version}
|
||||
@msg {name} installed: {version} {name} installé : {version}
|
||||
@msg {name} updated to {version} (this release publishes no checksum, so only its size was checked) {name} mis à jour vers {version} (cette version ne publie pas de somme de contrôle : seule sa taille a été vérifiée)
|
||||
@msg {name} installed: {version} (this release publishes no checksum, so only its size was checked) {name} installé : {version} (cette version ne publie pas de somme de contrôle : seule sa taille a été vérifiée)
|
||||
@msg {name} was removed {name} a été supprimé
|
||||
@msg {name} is running. Quit it, then try again. {name} est en cours d'exécution. Quittez-le, puis réessayez.
|
||||
|
||||
# Command IDs for menu items (@id)
|
||||
# Format: @id<TAB>command_id<TAB>translation
|
||||
@id app.install Installer
|
||||
@id app.update Mettre à jour vers {v}
|
||||
@id app.open Ouvrir
|
||||
@id app.download Télécharger pour {os}
|
||||
@id app.reveal Afficher dans le Finder
|
||||
@id app.reinstall Réinstaller
|
||||
@id app.uninstall Désinstaller…
|
||||
@id app.notes Notes de version
|
||||
@id app.website Site web
|
||||
@id app.github Dépôt GitHub
|
||||
@id app.releases Toutes les versions
|
||||
@id launcher.update Mettre à jour et redémarrer
|
||||
@id launcher.download Télécharger
|
||||
@id launcher.notes Nouveautés
|
||||
@id launcher.dismiss Ignorer
|
||||
@id settings.open Réglages
|
||||
@id settings.quit Quitter ArtCraft Launcher
|
||||
@id view.grid Grille
|
||||
@id view.table Tableau
|
||||
@id filter.all Toutes
|
||||
@id filter.installed Installées
|
||||
@id filter.updates Mises à jour
|
||||
@id filter.notinstalled Non installées
|
||||
@id sort.suite Ordre de la suite
|
||||
@id sort.name Nom
|
||||
@id sort.inspired Inspirée de
|
||||
@id sort.status État
|
||||
@id sort.installed Version installée
|
||||
@id sort.latest Dernière version
|
||||
@id sort.released Version la plus récente
|
||||
@id help.discord Discord
|
||||
@id help.github GitHub
|
||||
@id help.website Site web
|
||||
@id update.check Rechercher des mises à jour
|
||||
@id update.check_auto Rechercher automatiquement les mises à jour
|
||||
@id update.notify Me prévenir quand une application installée a une mise à jour
|
||||
@id update.prereleases Inclure les préversions (bêtas et versions candidates)
|
||||
@id linux.preferred_kind Installer les nouvelles applications en tant que
|
||||
@id tray.close_to_tray Rester actif dans la zone de notification à la fermeture de la fenêtre
|
||||
@id tray.show_on_current_desktop Afficher la fenêtre sur le bureau actuel
|
||||
@id tray.login_item Lancer ArtCraft Launcher à l'ouverture de session
|
||||
@id appearance.theme Apparence
|
||||
|
||||
# Tray (menu bar) menu and tooltip
|
||||
Show ArtCraft Launcher Afficher ArtCraft Launcher
|
||||
Check for Updates Rechercher des mises à jour
|
||||
Visit getartcraft.com Visiter getartcraft.com
|
||||
ArtCraft on GitHub ArtCraft sur GitHub
|
||||
Join the ArtCraft Discord Rejoindre le Discord d'ArtCraft
|
||||
@plural ArtCraft Launcher · {n} update available|ArtCraft Launcher · {n} updates available ArtCraft Launcher · {n} mise à jour disponible|ArtCraft Launcher · {n} mises à jour disponibles
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 50.
|
@@ -7,7 +7,7 @@ use super::detect::system_lang;
|
||||
|
||||
/// One supported UI language.
|
||||
pub struct LangInfo {
|
||||
/// Canonical BCP 47 code (`en`, `ru`, `zh-CN`, `de`). Also the `interfaceLanguage` value.
|
||||
/// Canonical BCP 47 code (`en`, `ru`, `zh-CN`, `de`, `fr`). Also the `interfaceLanguage` value.
|
||||
pub code: &'static str,
|
||||
/// The language's name in itself, shown in the Preferences dropdown.
|
||||
pub name: &'static str,
|
||||
@@ -18,8 +18,8 @@ pub struct LangInfo {
|
||||
pub cjk: bool,
|
||||
/// Catalog file contents (empty for the built-in English).
|
||||
pub source: &'static str,
|
||||
/// Plural form index for a count (English and German: 0 = one, 1 = other; Russian: 0 = one, 1 = few,
|
||||
/// 2 = many; Chinese: always 0).
|
||||
/// Plural form index for a count (English and German: 0 = one, 1 = other; Russian: 0 = one,
|
||||
/// 1 = few, 2 = many; French: 0 = one for 0 and 1; Chinese: always 0).
|
||||
pub plural: fn(u64) -> usize,
|
||||
/// Number of plural forms the catalog's `@plural` rows carry (checked by tests).
|
||||
pub plural_forms: usize,
|
||||
@@ -32,6 +32,11 @@ fn plural_one_other(n: u64) -> usize {
|
||||
usize::from(n != 1)
|
||||
}
|
||||
|
||||
/// French: 0 and 1 are singular ("0 mise à jour"), everything else plural.
|
||||
fn plural_zero_one(n: u64) -> usize {
|
||||
usize::from(n > 1)
|
||||
}
|
||||
|
||||
/// Languages whose nouns don't inflect for number (Chinese): one form for every count.
|
||||
fn plural_none(_: u64) -> usize {
|
||||
0
|
||||
@@ -51,7 +56,7 @@ pub(super) fn plural_east_slavic(n: u64) -> usize {
|
||||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 4] = [
|
||||
pub static LANGUAGES: [LangInfo; 5] = [
|
||||
LangInfo {
|
||||
code: "en",
|
||||
name: "English",
|
||||
@@ -96,6 +101,17 @@ pub static LANGUAGES: [LangInfo; 4] = [
|
||||
complete_menus: true,
|
||||
catalog: OnceLock::new(),
|
||||
},
|
||||
LangInfo {
|
||||
code: "fr",
|
||||
name: "Français",
|
||||
english_name: "French",
|
||||
cjk: false,
|
||||
source: include_str!("fr.tsv"),
|
||||
plural: plural_zero_one,
|
||||
plural_forms: 2,
|
||||
complete_menus: true,
|
||||
catalog: OnceLock::new(),
|
||||
},
|
||||
];
|
||||
|
||||
impl LangInfo {
|
||||
@@ -127,6 +143,7 @@ impl Lang {
|
||||
pub const RU: Lang = Lang(&LANGUAGES[1]);
|
||||
pub const ZH_CN: Lang = Lang(&LANGUAGES[2]);
|
||||
pub const DE: Lang = Lang(&LANGUAGES[3]);
|
||||
pub const FR: Lang = Lang(&LANGUAGES[4]);
|
||||
|
||||
pub fn code(self) -> &'static str {
|
||||
self.0.code
|
||||
|
||||
@@ -249,6 +249,19 @@ fn german_counts_dates_and_messages_work() {
|
||||
assert_eq!(tr(Lang::DE, "Untranslated diagnostic"), "Untranslated diagnostic");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn french_locales_and_plurals_work() {
|
||||
for tag in ["fr", "fr-FR", "fr_CA.UTF-8", "fr-BE", "FR_ch"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(Lang::FR), "{tag}");
|
||||
}
|
||||
assert_eq!(super::detect::first_supported(["xx-XX", "fr-FR", "en-US"]), Some(Lang::FR));
|
||||
// 0 and 1 are singular in French, unlike English.
|
||||
assert_eq!(trn(Lang::FR, 0, "{n} app", "{n} apps"), "0 application");
|
||||
assert_eq!(trn(Lang::FR, 1, "{n} app", "{n} apps"), "1 application");
|
||||
assert_eq!(trn(Lang::FR, 2, "{n} app", "{n} apps"), "2 applications");
|
||||
assert_eq!(trn(Lang::FR, 3, "{n} day ago", "{n} days ago"), "il y a 3 jours");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simplified_chinese_locales_respect_the_script() {
|
||||
for tag in ["zh", "zh-CN", "zh_CN.UTF-8", "zh-SG", "zh-Hans", "zh-Hans-CN", "zh-Hans-HK", "ZH_cn"] {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
//! The French UI, in its own test binary: the UI language is process-wide, so it can't share one
|
||||
//! with the English UI tests. `ARTCRAFT_LAUNCHER_SNAPSHOTS=<dir>` also saves screenshots (run with
|
||||
//! `-- --test-threads=1`, see `ui.rs`).
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use artcraft_launcher_core::protocol::{Backend, Capabilities, Command, Event, JobPhase, LatestRelease, Origin, Source};
|
||||
use artcraft_launcher_core::release::{Asset, Release};
|
||||
use artcraft_launcher_core::status::InstallSource;
|
||||
use artcraft_launcher_core::{Arch, Installed, Os, Platform, SUITE, version};
|
||||
use artcraft_launcher_ui_egui::settings::ViewMode;
|
||||
use artcraft_launcher_ui_egui::{Host, LauncherUi, Settings};
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
|
||||
const NOW: i64 = 1_791_500_000;
|
||||
|
||||
struct Fake {
|
||||
events: Vec<Event>,
|
||||
clock: Rc<Cell<i64>>,
|
||||
}
|
||||
|
||||
impl Backend for Fake {
|
||||
fn send(&mut self, _command: Command) {}
|
||||
fn poll(&mut self) -> Vec<Event> {
|
||||
std::mem::take(&mut self.events)
|
||||
}
|
||||
fn capabilities(&self) -> Capabilities {
|
||||
Capabilities { manage_apps: true, reveal_label: "Show in Finder", self_update: true }
|
||||
}
|
||||
fn platform(&self) -> Option<Platform> {
|
||||
Some(Platform { os: Os::MacOs, arch: Arch::Arm64 })
|
||||
}
|
||||
fn now(&self) -> i64 {
|
||||
self.clock.get()
|
||||
}
|
||||
}
|
||||
|
||||
struct NoHost;
|
||||
impl Host for NoHost {}
|
||||
|
||||
fn release(id: &str, v: &str, at: i64) -> Release {
|
||||
let name = format!("{id}-{v}-macos-universal.dmg");
|
||||
Release {
|
||||
tag: format!("v{v}"),
|
||||
version: version::parse(v),
|
||||
name: format!("v{v}"),
|
||||
published_at: Some(at),
|
||||
html_url: format!("https://github.com/storytold/{id}/releases/tag/v{v}"),
|
||||
notes: String::new(),
|
||||
prerelease: false,
|
||||
assets: vec![Asset {
|
||||
name: name.clone(),
|
||||
size: 150 << 20,
|
||||
url: format!("https://github.com/storytold/{id}/releases/download/v{v}/{name}"),
|
||||
sha256: None,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
fn events() -> Vec<Event> {
|
||||
let mut ev = Vec::new();
|
||||
for (i, app) in SUITE.iter().enumerate() {
|
||||
let latest = LatestRelease::Found(release(app.id, "0.5.0", NOW - 40 * 86_400 - i as i64 * 600));
|
||||
ev.push(Event::Latest { app: app.id.into(), latest, from: Origin::ArtCraftApi });
|
||||
let installed = matches!(app.id, "photocraft" | "lightcraft").then(|| Installed {
|
||||
version: version::parse(if app.id == "photocraft" { "0.4.1" } else { "0.5.0" }),
|
||||
source: InstallSource::MacApp,
|
||||
location: format!("/Applications/{}.app", app.name),
|
||||
package: None,
|
||||
});
|
||||
ev.push(Event::Detected { app: app.id.into(), installed });
|
||||
}
|
||||
ev.push(Event::Progress { app: "filmcraft".into(), phase: JobPhase::Verifying });
|
||||
ev.push(Event::CheckFinished { at: NOW - 180, source: Source::Feed, rate_limited_until: None });
|
||||
// A backend message, translated through its `@msg` template where it's shown.
|
||||
ev.push(Event::JobFinished { app: "lightcraft".into(), result: Ok("LightCraft updated to 0.5.0".into()) });
|
||||
ev
|
||||
}
|
||||
|
||||
fn harness(view: ViewMode) -> Harness<'static, Option<LauncherUi>> {
|
||||
artcraft_launcher_ui_egui::i18n::set_test_lang(artcraft_launcher_ui_egui::i18n::Lang::EN);
|
||||
let mut backend = Some(Fake { events: events(), clock: Rc::new(Cell::new(NOW)) });
|
||||
let settings =
|
||||
Settings { view, notify: false, language: "fr".into(), theme: artcraft_launcher_ui_egui::theme::ThemeChoice::Dark, ..Settings::default() };
|
||||
Harness::builder().with_size(egui::vec2(1180.0, 900.0)).with_theme(egui::Theme::Dark).build_ui_state(
|
||||
move |ui, state: &mut Option<LauncherUi>| {
|
||||
if state.is_none()
|
||||
&& let Some(b) = backend.take()
|
||||
{
|
||||
*state = Some(LauncherUi::new(ui.ctx(), Box::new(b), settings.clone()));
|
||||
}
|
||||
if let Some(l) = state.as_mut() {
|
||||
l.logic(&ui.ctx().clone(), &mut NoHost);
|
||||
l.ui(ui, &mut NoHost);
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn snapshot(h: &mut Harness<'_, Option<LauncherUi>>, name: &str) {
|
||||
let Some(dir) = std::env::var_os("ARTCRAFT_LAUNCHER_SNAPSHOTS") else { return };
|
||||
let img = h.render().unwrap();
|
||||
img.save(std::path::Path::new(&dir).join(format!("{name}.png"))).unwrap();
|
||||
}
|
||||
|
||||
// One test: the UI language is process-wide.
|
||||
#[test]
|
||||
fn french_ui_translates_labels_dates_and_messages() {
|
||||
let mut h = harness(ViewMode::Grid);
|
||||
h.run_steps(8);
|
||||
snapshot(&mut h, "grid-fr-dark");
|
||||
// Product names stay in English.
|
||||
h.get_by_label("Launcher");
|
||||
h.get_by_label("Craft App Suite");
|
||||
h.get_by_label("Retouche d'images");
|
||||
h.get_by_label("Mettre à jour vers 0.5.0");
|
||||
h.get_by_label_contains("Vérification de la somme de contrôle…");
|
||||
h.get_by_label_contains("Vérifié il y a 3 min · via ArtCraft API");
|
||||
h.get_by_label("LightCraft mis à jour vers 0.5.0");
|
||||
|
||||
let mut table = harness(ViewMode::Table);
|
||||
table.run_steps(8);
|
||||
snapshot(&mut table, "table-fr-dark");
|
||||
table.get_by_label("Installée");
|
||||
assert!(table.query_all_by_label_contains("août 2026").count() >= 1);
|
||||
}
|
||||
Reference in new issue
Block a user