diff --git a/package.json b/package.json index 18ca4ab..92d7ae2 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "backgroundColor": "transparent", "languages": [ "zh-CN", - "en-US" + "en-US", + "fr-FR" ], "showNameOnTiles": true, "setBuildNumber": true diff --git a/src/components/settings/app.tsx b/src/components/settings/app.tsx index 1c020a2..9d02b78 100644 --- a/src/components/settings/app.tsx +++ b/src/components/settings/app.tsx @@ -80,7 +80,8 @@ class AppTab extends React.Component { languageOptions = (): IDropdownOption[] => [ { key: "default", text: intl.get("followSystem") }, { key: "en-US", text: "English" }, - { key: "zh-CN", text: "中文(简体)"} + { key: "zh-CN", text: "中文(简体)"}, + { key: "fr-FR", text: "Français"}, ] toggleStatus = () => { diff --git a/src/components/settings/sources.tsx b/src/components/settings/sources.tsx index 7d866ba..ab54576 100644 --- a/src/components/settings/sources.tsx +++ b/src/components/settings/sources.tsx @@ -82,14 +82,14 @@ class SourcesTab extends React.Component { fetchFrequencyOptions = (): IDropdownOption[] => [ { key: "0", text: intl.get("sources.unlimited") }, - { key: "15", text: intl.get("time.m", { m: 15 }) }, - { key: "30", text: intl.get("time.m", { m: 30 }) }, - { key: "60", text: intl.get("time.h", { h: 1 }) }, - { key: "120", text: intl.get("time.h", { h: 2 }) }, - { key: "180", text: intl.get("time.h", { h: 3 }) }, - { key: "360", text: intl.get("time.h", { h: 6 }) }, - { key: "720", text: intl.get("time.h", { h: 12 }) }, - { key: "1440", text: intl.get("time.d", { d: 1 }) } + { key: "15", text: intl.get("time.minute", { m: 15 }) }, + { key: "30", text: intl.get("time.minute", { m: 30 }) }, + { key: "60", text: intl.get("time.hour", { h: 1 }) }, + { key: "120", text: intl.get("time.hour", { h: 2 }) }, + { key: "180", text: intl.get("time.hour", { h: 3 }) }, + { key: "360", text: intl.get("time.hour", { h: 6 }) }, + { key: "720", text: intl.get("time.hour", { h: 12 }) }, + { key: "1440", text: intl.get("time.day", { d: 1 }) } ] onFetchFrequencyChange = (_, option: IDropdownOption) => { diff --git a/src/components/utils/time.tsx b/src/components/utils/time.tsx index 6d59d2c..01e53a4 100644 --- a/src/components/utils/time.tsx +++ b/src/components/utils/time.tsx @@ -1,4 +1,5 @@ import * as React from "react" +import intl = require("react-intl-universal") interface TimeProps { date: Date @@ -26,10 +27,10 @@ class Time extends React.Component { displayTime(past: Date, now: Date): string { // difference in seconds let diff = (now.getTime() - past.getTime()) / 60000 - if (diff < 1) return "now" - else if (diff < 60) return Math.floor(diff) + "m" - else if (diff < 1440) return Math.floor(diff / 60) + "h" - else return Math.floor(diff / 1440) + "d" + if (diff < 1) return intl.get("time.now") + else if (diff < 60) return Math.floor(diff) + intl.get("time.m") + else if (diff < 1440) return Math.floor(diff / 60) + intl.get("time.h") + else return Math.floor(diff / 1440) + intl.get("time.d") } render() { diff --git a/src/scripts/i18n/_locales.ts b/src/scripts/i18n/_locales.ts index abcfbd4..852e47c 100644 --- a/src/scripts/i18n/_locales.ts +++ b/src/scripts/i18n/_locales.ts @@ -1,9 +1,11 @@ import en_US from "./en-US.json" import zh_CN from "./zh-CN.json" +import fr_FR from "./fr-FR.json" const locales = { "en-US": en_US, "zh-CN": zh_CN, + "fr-FR": fr_FR, } export default locales \ No newline at end of file diff --git a/src/scripts/i18n/en-US.json b/src/scripts/i18n/en-US.json index f027d11..807e632 100644 --- a/src/scripts/i18n/en-US.json +++ b/src/scripts/i18n/en-US.json @@ -19,9 +19,13 @@ "confirm": "Confirm", "cancel": "Cancel", "time": { - "m": "{m, plural, =1 {# minute} other {# minutes}}", - "h": "{h, plural, =1 {# hour} other {# hours}}", - "d": "{d, plural, =1 {# day} other {# days}}" + "now": "now", + "m": "m", + "h": "h", + "d": "d", + "minute": "{m, plural, =1 {# minute} other {# minutes}}", + "hour": "{h, plural, =1 {# hour} other {# hours}}", + "day": "{d, plural, =1 {# day} other {# days}}" }, "log": { "empty": "No notifications", diff --git a/src/scripts/i18n/fr-FR.json b/src/scripts/i18n/fr-FR.json new file mode 100644 index 0000000..33be189 --- /dev/null +++ b/src/scripts/i18n/fr-FR.json @@ -0,0 +1,152 @@ +{ + "allArticles": "Tous les articles", + "add": "Ajouter", + "create": "Créer", + "icon": "Icône", + "name": "Nom", + "openExternal": "Ouvrir dans le navigateur", + "emptyName": "Ce champ ne peut pas être vide.", + "followSystem": "Suivre le système", + "more": "Plus", + "close": "Fermer", + "search": "Rechercher", + "loadMore": "Charger plus", + "dangerButton": "Confirmer {action} ?", + "confirmMarkAll": "Voulez-vous vraiment marquer tous les articles de cette page comme lus ?", + "confirm": "Confirmer", + "cancel": "Annuler", + "time": { + "now": "maintenant", + "m": "m", + "h": "h", + "d": "j", + "minute": "{m, plural, =1 {# minute} other {# minutes}}", + "hour": "{h, plural, =1 {# heure} other {# heures}}", + "day": "{d, plural, =1 {# jour} other {# jours}}" + }, + "log": { + "empty": "Aucune notification", + "fetchFailure": "Échec du chargement de la source \"{name}\".", + "fetchSuccess": "Récupération réussi de {count, plural, =1 {# article} other {# articles}}." + }, + "nav": { + "menu": "Menu", + "refresh": "Actualiser", + "markAllRead": "Marquer tout comme lu", + "notifications": "Notifications", + "view": "Affichage", + "settings": "Paramètres", + "minimize": "Réduire", + "maximize": "Agrandir" + }, + "menu": { + "close": "Fermer le menu", + "subscriptions": "Abonnements" + }, + "article": { + "empty": "Pas d'articles", + "untitled": "(Sans titre)", + "hide": "Masquer l'article", + "unhide": "Afficher l'article", + "markRead": "Marquer comme lu", + "markUnread": "Marquer comme non lu", + "star": "Mettre en favori", + "unstar": "Supprimer le favori", + "fontSize": "Taille de la police", + "loadWebpage": "Charger la page web" + }, + "context": { + "read": "Lu", + "copyTitle": "Copier le titre", + "copyURL": "Copier le lien", + "copy": "Copier", + "search": "Rechercher \"{text}\" sur Google", + "view": "Affichage", + "cardView": "Vue par carte", + "listView": "vue par liste", + "filter": "Filtrer", + "unreadOnly": "Non lu uniquement", + "starredOnly": "Favoris uniquement", + "fullSearch": "Recherche dans le texte complet", + "showHidden": "Afficher les articles cachés", + "manageSources": "Gérer les sources" + }, + "settings": { + "writeError": "Une erreur s'est produite lors de l'écriture du fichier.", + "name": "Paramètres", + "fetching": "Mise à jour des sources, veuillez patienter…", + "exit": "Fermer les paramètres", + "sources": "Sources", + "grouping": "Groupes", + "app": "Préférences", + "about": "À propos", + "version": "Version", + "shortcuts": "Raccourcis", + "openSource": "Open source", + "feedback": "Feedback" + }, + "sources": { + "untitled": "Source", + "errorAdd": "Une erreur s'est produite lors de l'ajout de la source.", + "errorImport": "Erreur d'importation pour {count, plural, =1 {# source} other {# sources}}.", + "opmlFile": "Fichier OPML", + "name": "Nom de la source", + "editName": "Modifier le nom", + "fetchFrequency": "Limitation de la fréquence de collecte des données", + "unlimited": "Illimité", + "openTarget": "Mode d'ouverture par défaut des articles", + "delete": "Supprimer la source", + "add": "Ajouter la source", + "import": "Importer", + "export": "Exporter", + "rssText": "Texte complet RSS", + "loadWebpage": "Charger la page web", + "inputUrl": "Saisissez l'adresse URL", + "badUrl": "Adresse URL invalide", + "deleteWarning": "La source et tous les articles sauvegardés seront supprimés.", + "selected": "Source sélectionnée" + }, + "groups": { + "type": "Type", + "group": "Groupe", + "source": "Source", + "capacity": "Contenu", + "exitGroup": "Retour aux groupes", + "deleteSource": "Supprimer du groupe", + "sourceHint": "Glisser-déposer les sources pour les réorganiser.", + "create": "Créer un groupe", + "selectedGroup": "Groupe sélectionné", + "selectedSource": "Source sélectionnée", + "enterName": "Saisissez un nom", + "editName": "Modifier le nom", + "deleteGroup": "Supprimer le groupe", + "chooseGroup": "Selectionner un groupe", + "addToGroup": "Ajouter à ...", + "groupHint": "Double-cliquez sur le groupe pour modifier ses sources. Faites un glisser-déposer pour réorganiser les sources." + }, + "app": { + "cleanup": "Nettoyage", + "cache": "Supprimer le cache", + "cacheSize": "{size} de données mises en cache", + "deleteChoices": "Supprimer les articles antérieurs à ... jours", + "confirmDelete": "Supprimer", + "daysAgo": "{days} jours", + "deleteAll": "Supprimer tous les articles", + "calculatingSize": "Calcul de la taille...", + "itemSize": "Environ {size} du stockage local est occupé par des articles", + "confirmImport": "Voulez-vous vraiment importer des données du fichier de sauvegarde ? Toutes les données actuelles seront effacées.", + "data": "Données de l'appplication", + "backup": "Sauvegarder", + "restore": "Restorer", + "frData": "Données de Fluent Reader", + "language": "Langue d'affichage", + "theme": "Thème", + "lightTheme": "Mode jour", + "darkTheme": "Mode nuit", + "enableProxy": "Activer le Proxy", + "badUrl": "Adresse URL invalide", + "pac": "Adresse PAC", + "setPac": "Définir PAC", + "pacHint": "Pour les proxies Sockets, il est recommandé que le PAC retourne \"SOCKS5\" pour le DNS côté proxies. La désactivation du proxy nécessite un redémarrage." + } +} \ No newline at end of file diff --git a/src/scripts/i18n/zh-CN.json b/src/scripts/i18n/zh-CN.json index da77fc9..bf7da01 100644 --- a/src/scripts/i18n/zh-CN.json +++ b/src/scripts/i18n/zh-CN.json @@ -19,9 +19,13 @@ "confirm": "确认", "cancel": "取消", "time": { - "m": "{m}分钟", - "h": "{h}小时", - "d": "{d}天" + "now": "now", + "m": "m", + "h": "h", + "d": "d", + "minute": "{m}分钟", + "hour": "{h}小时", + "day": "{d}天" }, "log": { "empty": "无消息",