diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b797c17 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["inlang.vs-code-extension"] +} diff --git a/biome.json b/biome.json index 1115ca8..b5b3cec 100644 --- a/biome.json +++ b/biome.json @@ -75,6 +75,12 @@ "indentWidth": 4 }, "files": { - "ignore": ["node_modules/**/*", "dist/**/*", ".output", ".nuxt"] + "ignore": [ + "node_modules/**/*", + "dist/**/*", + ".output", + ".nuxt", + "paraglide" + ] } } diff --git a/bun.lockb b/bun.lockb index e967536..843f8a1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/notes/content.vue b/components/notes/content.vue index 72bcdb8..74c6d41 100644 --- a/components/notes/content.vue +++ b/components/notes/content.vue @@ -41,6 +41,7 @@ import { cn } from "@/lib/utils"; import type { Attachment, Emoji, Status } from "@versia/client/types"; import { TriangleAlert } from "lucide-vue-next"; import { Button } from "~/components/ui/button"; +import { languageTag } from "~/paraglide/runtime"; import { type BooleanSetting, SettingIds } from "~/settings"; import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import Attachments from "./attachments.vue"; @@ -75,7 +76,7 @@ const isOverflowing = computed(() => { const characterCount = plainContent?.length; const formattedCharacterCount = characterCount - ? new Intl.NumberFormat("en-us").format(characterCount) + ? new Intl.NumberFormat(languageTag()).format(characterCount) : undefined; diff --git a/components/notes/header.vue b/components/notes/header.vue index c382006..11c41bc 100644 --- a/components/notes/header.vue +++ b/components/notes/header.vue @@ -50,6 +50,7 @@ import type { UseTimeAgoUnitNamesDefault, } from "@vueuse/core"; import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next"; +import { languageTag } from "~/paraglide/runtime"; import { SettingIds } from "~/settings"; import Avatar from "../profiles/avatar.vue"; import SmallCard from "../profiles/small-card.vue"; @@ -89,7 +90,7 @@ const timeAgo = useTimeAgo(createdAt, { invalid: "", } as UseTimeAgoMessages, }); -const fullTime = new Intl.DateTimeFormat("en-US", { +const fullTime = new Intl.DateTimeFormat(languageTag(), { dateStyle: "medium", timeStyle: "short", }).format(createdAt); diff --git a/components/notes/menu.vue b/components/notes/menu.vue index cee51b7..d943c8d 100644 --- a/components/notes/menu.vue +++ b/components/notes/menu.vue @@ -84,7 +84,7 @@ const _delete = async () => { - + Note Actions diff --git a/components/notes/reblog-header.vue b/components/notes/reblog-header.vue index a5bb9e3..60013fe 100644 --- a/components/notes/reblog-header.vue +++ b/components/notes/reblog-header.vue @@ -3,13 +3,14 @@ {{ displayName }} - reblogged + {{ m.large_vivid_horse_catch() }} \ No newline at end of file diff --git a/components/profiles/profile-stats.vue b/components/profiles/profile-stats.vue index 567664a..4576717 100644 --- a/components/profiles/profile-stats.vue +++ b/components/profiles/profile-stats.vue @@ -3,20 +3,20 @@
- Joined {{ formattedCreationDate }} + {{ m.gross_fancy_platypus_seek() }} {{ formattedCreationDate }}
- {{ noteCount }} Notes + {{ noteCount }} {{ m.real_gray_stork_seek() }}
·
- {{ followerCount }} Followers + {{ followerCount }} {{ m.teal_helpful_parakeet_hike() }}
·
- {{ followingCount }} Following + {{ followingCount }} {{ m.aloof_royal_samuel_startle() }}
@@ -24,6 +24,8 @@ \ No newline at end of file diff --git a/components/sidebars/account-switcher.vue b/components/sidebars/account-switcher.vue index 1550f45..c8945fe 100644 --- a/components/sidebars/account-switcher.vue +++ b/components/sidebars/account-switcher.vue @@ -30,24 +30,24 @@ - Add account + {{ m.sunny_pink_hyena_walk() }} - + - Account + {{ m.factual_awful_hare_drip() }} - Log out + {{ m.sharp_big_mallard_reap() }} - Register + {{ m.honest_few_baboon_pop() }}
@@ -62,6 +62,7 @@ import { UserPlus, } from "lucide-vue-next"; import { toast } from "vue-sonner"; +import * as m from "~/paraglide/messages.js"; import { NuxtLink } from "#components"; import Avatar from "../profiles/avatar.vue"; import { Button } from "../ui/button"; diff --git a/components/sidebars/left-sidebar.vue b/components/sidebars/left-sidebar.vue index 9ccdf31..7995b1f 100644 --- a/components/sidebars/left-sidebar.vue +++ b/components/sidebars/left-sidebar.vue @@ -10,8 +10,8 @@ 'https://cdn.versia.pub/branding/icon.svg' " :name="instance?.title" />
- {{ instance?.title ?? 'Versia Server' }} - {{ "A Versia Server instance" }} + {{ instance?.title ?? m.short_zippy_felix_kick() }} + {{ m.top_active_ocelot_cure() }}
@@ -21,7 +21,7 @@ - Navigation + {{ m.trite_real_sawfish_drum() }} - More + {{ m.close_short_kitten_coax() }} @@ -73,12 +73,12 @@ @@ -119,6 +119,8 @@ import { SidebarMenuSubItem, SidebarRail, } from "~/components/ui/sidebar"; +import * as m from "~/paraglide/messages.js"; +import { setLanguageTag } from "~/paraglide/runtime"; import { type EnumSetting, SettingIds } from "~/settings"; import Avatar from "../profiles/avatar.vue"; import { Button } from "../ui/button"; @@ -126,31 +128,33 @@ import AccountSwitcher from "./account-switcher.vue"; const sidebarStyle = useSetting(SettingIds.SidebarStyle) as Ref; +setLanguageTag("fr"); + const data = { navMain: [ { - title: "Preferences", + title: m.patchy_seemly_hound_grace(), url: "/preferences", icon: Settings2, items: [ { - title: "Account", + title: m.factual_arable_jurgen_endure(), url: "/preferences/account", }, { - title: "Appearance", + title: m.tough_clean_wolf_gleam(), url: "/preferences/appearance", }, { - title: "Behaviour", + title: m.legal_best_tadpole_rise(), url: "/preferences/behaviour", }, { - title: "Emojis", + title: m.novel_trite_sloth_adapt(), url: "/preferences/emojis", }, { - title: "Roles", + title: m.safe_green_mink_cook(), url: "/preferences/roles", }, ], @@ -158,31 +162,31 @@ const data = { ], other: [ { - name: "Home", + name: m.bland_chunky_sparrow_propel(), url: "/home", icon: House, requiresLogin: true, }, { - name: "Public", + name: m.lost_trick_dog_grace(), url: "/public", icon: MapIcon, requiresLogin: false, }, { - name: "Local", + name: m.crazy_game_parrot_pave(), url: "/local", icon: BedSingle, requiresLogin: false, }, { - name: "Global", + name: m.real_tame_moose_greet(), url: "/global", icon: Globe, requiresLogin: false, }, { - name: "Notifications", + name: m.that_patchy_mare_snip(), url: "/notifications", icon: Bell, requiresLogin: true, diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..b5cd8ed --- /dev/null +++ b/messages/en.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "bland_chunky_sparrow_propel": "Home", + "lost_trick_dog_grace": "Public", + "crazy_game_parrot_pave": "Local", + "real_tame_moose_greet": "Global", + "that_patchy_mare_snip": "Notifications", + "patchy_seemly_hound_grace": "Preferences", + "factual_arable_jurgen_endure": "Account", + "tough_clean_wolf_gleam": "Appearance", + "legal_best_tadpole_rise": "Behaviour", + "novel_trite_sloth_adapt": "Emojis", + "safe_green_mink_cook": "Roles", + "short_zippy_felix_kick": "Versia Server", + "top_active_ocelot_cure": "A Versia Server instance", + "trite_real_sawfish_drum": "Navigation", + "close_short_kitten_coax": "More", + "salty_aloof_turkey_nudge": "Compose", + "quaint_low_felix_pave": "Update", + "sunny_pink_hyena_walk": "Add account", + "factual_awful_hare_drip": "Account", + "sharp_big_mallard_reap": "Log out", + "honest_few_baboon_pop": "Register", + "spicy_loved_giraffe_empower": "Profile Actions", + "cool_dark_tapir_belong": "Copy username", + "yummy_moving_scallop_sail": "Copy API data", + "sunny_zany_jellyfish_pop": "Copy ID", + "ago_new_pelican_drip": "Copy link", + "solid_witty_zebra_walk": "Copy link (origin)", + "active_trite_lark_inspire": "Open on remote", + "spare_wild_mole_intend": "Mute", + "misty_soft_sparrow_vent": "Block", + "slow_chunky_chipmunk_hush": "Refresh", + "great_few_jaguar_rise": "Report", + "flat_nice_worm_dream": "Copied to clipboard", + "ornate_tidy_coyote_grow": "Muting user...", + "empty_smug_raven_bloom": "Blocking user...", + "real_every_macaw_wish": "Requesting refresh...", + "many_cool_fox_love": "Account refreshed", + "gross_fancy_platypus_seek": "Joined", + "real_gray_stork_seek": "Notes", + "teal_helpful_parakeet_hike": "Followers", + "aloof_royal_samuel_startle": "Following", + "brief_upper_otter_cuddle": "Unfollow", + "weak_bright_larva_grasp": "Requested", + "lazy_major_loris_grasp": "Follow", + "honest_jolly_shell_blend": "This user is a Versia developer.", + "nice_bad_grizzly_coax": "Versia Developer", + "merry_red_shrimp_bump": "Automated", + "sweet_mad_jannes_create": "This account is not operated as living entity.", + "many_fair_capybara_imagine": "Follow user", + "vivid_each_warthog_edit": "Are you sure you want to follow @${account.acct}?", + "cuddly_even_tern_loop": "Follow", + "soft_bold_ant_attend": "Cancel", + "quick_basic_peacock_bubble": "Following user...", + "awake_quick_cuckoo_smile": "User followed", + "funny_aloof_swan_loop": "Unfollow user", + "cute_polite_oryx_blend": "Unfollow", + "dirty_inclusive_meerkat_nudge": "Cancel", + "big_safe_guppy_mix": "Unfollowing user...", + "misty_level_stingray_expand": "User unfollowed", + "lime_day_squid_pout": "Global", + "witty_heroic_trout_cry": "Uploaded by you", + "cuddly_such_swallow_hush": "Rename", + "tense_quick_cod_favor": "Delete", + "slimy_awful_florian_sail": "Enter a new shortcode", + "teary_antsy_panda_aid": "Edit", + "teary_tame_gull_bless": "Updating shortcode...", + "gaudy_lime_bison_adore": "Shortcode updated.", + "weary_away_liger_zip": "Deleting emoji...", + "crisp_whole_canary_tear": "Emoji deleted.", + "mellow_yummy_jannes_cuddle": "Are you sure you want to follow {acct}?", + "white_best_dolphin_catch": "Are you sure you want to unfollow {acct}?", + "bright_late_osprey_renew": "Banner", + "great_level_lamb_sway": "Recommended size: over 1500x500px", + "safe_icy_bulldog_quell": "Avatar", + "aware_quiet_opossum_catch": "Recommended size: 400x400px", + "mild_known_mallard_jolt": "Display Name", + "lime_dry_skunk_loop": "Custom emojis can be used here.", + "neat_silly_dog_prosper": "Username", + "petty_plane_tadpole_earn": "Changing this will break all links to your profile.", + "next_caring_ladybug_hack": "Bio", + "stale_just_anaconda_earn": "Markdown and custom emojis are supported.", + "aqua_mealy_toucan_pride": "Custom Fields", + "front_north_eel_gulp": "Add field", + "gaudy_each_opossum_play": "Mark account as bot", + "grassy_acidic_gadfly_cure": "Is this account sending automated messages?", + "dirty_moving_shark_emerge": "Enable follow requests", + "bright_fun_mouse_boil": "Will require approval for new followers.", + "red_vivid_cuckoo_spark": "Allow account discovery", + "plain_zany_donkey_dart": "Allow your account to be found in search results.", + "jolly_noble_sloth_breathe": "Updating profile...", + "tough_alive_niklas_promise": "No changes", + "spry_honest_kestrel_arrive": "Profile updated", + "civil_icy_ant_mend": "Banner must be less than {size} bytes", + "zippy_caring_raven_edit": "Avatar must be less than {size} bytes", + "still_upper_otter_dine": "Username can only contain lowercase letters, numbers, underscores and hyphens", + "aware_house_dolphin_win": "Must be at least 3 characters long", + "weary_fresh_dragonfly_bless": "Must be a valid email address", + "sunny_novel_otter_glow": "Must be at least 3 characters long", + "fluffy_soft_wolf_cook": "Email (or username)", + "livid_bright_wallaby_quiz": "Password", + "fuzzy_sea_moth_absorb": "Sign In", + "tidy_tidy_cow_cut": "Or continue with", + "slow_these_kestrel_sail": "Accept", + "weary_steep_yak_embrace": "Reject", + "cool_slimy_coyote_affirm": "Accepting follow request...", + "busy_awful_mouse_jump": "Follow request accepted.", + "front_sunny_penguin_flip": "Rejecting follow request...", + "green_flat_mayfly_trust": "Follow request rejected.", + "large_vivid_horse_catch": "reblogged" +} diff --git a/messages/fr.json b/messages/fr.json new file mode 100644 index 0000000..28e0006 --- /dev/null +++ b/messages/fr.json @@ -0,0 +1,109 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "bland_chunky_sparrow_propel": "Acceuil", + "lost_trick_dog_grace": "Public", + "crazy_game_parrot_pave": "Local", + "real_tame_moose_greet": "Global", + "that_patchy_mare_snip": "Notifications", + "patchy_seemly_hound_grace": "Préférences", + "factual_arable_jurgen_endure": "Compte", + "tough_clean_wolf_gleam": "Apparence", + "legal_best_tadpole_rise": "Comportement", + "novel_trite_sloth_adapt": "Émojis", + "safe_green_mink_cook": "Rôles", + "short_zippy_felix_kick": "Versia Server", + "top_active_ocelot_cure": "Une instance de Versia Server", + "trite_real_sawfish_drum": "Navigation", + "close_short_kitten_coax": "Plus", + "salty_aloof_turkey_nudge": "Composer", + "quaint_low_felix_pave": "Mettre à jour", + "sunny_pink_hyena_walk": "Ajouter un compte", + "factual_awful_hare_drip": "Compte", + "sharp_big_mallard_reap": "Se déconnecter", + "honest_few_baboon_pop": "Créer un compte", + "spicy_loved_giraffe_empower": "Actions du profil", + "cool_dark_tapir_belong": "Copier le nom d'utilisateur", + "yummy_moving_scallop_sail": "Copier les données de l'API", + "sunny_zany_jellyfish_pop": "Copier l'ID", + "ago_new_pelican_drip": "Copier le lien", + "solid_witty_zebra_walk": "Copier le lien (origine)", + "active_trite_lark_inspire": "Ouvrir l'origine", + "spare_wild_mole_intend": "Muter", + "misty_soft_sparrow_vent": "Bloquer", + "slow_chunky_chipmunk_hush": "Rafraîchir", + "great_few_jaguar_rise": "Signaler", + "flat_nice_worm_dream": "Copié dans le presse-papiers", + "ornate_tidy_coyote_grow": "Mutage de l'utilisateur...", + "empty_smug_raven_bloom": "Blocage de l'utilisateur...", + "real_every_macaw_wish": "Demande d'actualisation...", + "many_cool_fox_love": "Compte actualisé", + "gross_fancy_platypus_seek": "Inscrit en", + "real_gray_stork_seek": "Notes", + "teal_helpful_parakeet_hike": "Abonné•e•s", + "aloof_royal_samuel_startle": "Abonnements", + "brief_upper_otter_cuddle": "Se désabonner", + "weak_bright_larva_grasp": "Demandé", + "lazy_major_loris_grasp": "Suivre", + "honest_jolly_shell_blend": "Cet utilisateur est un développeur Versia.", + "nice_bad_grizzly_coax": "Développeur Versia", + "merry_red_shrimp_bump": "Automatisé", + "sweet_mad_jannes_create": "Ce compte n'est pas utilisé par une entité vivante.", + "many_fair_capybara_imagine": "Suivre", + "cuddly_even_tern_loop": "Suivre", + "soft_bold_ant_attend": "Annuler", + "quick_basic_peacock_bubble": "Abonnement en cours...", + "awake_quick_cuckoo_smile": "Utilisateur suivi", + "funny_aloof_swan_loop": "Se désabonner", + "cute_polite_oryx_blend": "Se désabonner", + "dirty_inclusive_meerkat_nudge": "Annuler", + "big_safe_guppy_mix": "Désabonnement...", + "misty_level_stingray_expand": "Utilisateur désabonné", + "lime_day_squid_pout": "Global", + "witty_heroic_trout_cry": "Ajouté par vous", + "cuddly_such_swallow_hush": "Renommer", + "tense_quick_cod_favor": "Supprimer", + "slimy_awful_florian_sail": "Entrez un nouveau nom", + "teary_antsy_panda_aid": "Modifier", + "teary_tame_gull_bless": "Mise à jour du nom...", + "gaudy_lime_bison_adore": "Nom mis à jour.", + "weary_away_liger_zip": "Suppression de l'emoji...", + "crisp_whole_canary_tear": "Emoji supprimé.", + "mellow_yummy_jannes_cuddle": "Êtes-vous sûr de vouloir suivre {acct} ?", + "white_best_dolphin_catch": "Etes-vous sûr de vouloir vous désabonner de {acct} ?", + "bright_late_osprey_renew": "Bannière", + "great_level_lamb_sway": "Taille recommandée : plus de 1500x500px", + "safe_icy_bulldog_quell": "Avatar", + "aware_quiet_opossum_catch": "Taille recommandée : 400x400px", + "mild_known_mallard_jolt": "Nom d'affichage", + "lime_dry_skunk_loop": "Des émojis personnalisés peuvent être utilisés ici.", + "neat_silly_dog_prosper": "Nom d'utilisateur", + "petty_plane_tadpole_earn": "Changer ce nom brisera tous les liens vers votre profil.", + "next_caring_ladybug_hack": "Bio", + "stale_just_anaconda_earn": "Le Markdown et les émojis personnalisés sont utilisables.", + "aqua_mealy_toucan_pride": "Champs personnalisés", + "front_north_eel_gulp": "Ajouter un champ", + "dirty_moving_shark_emerge": "Activer les demandes de suivi", + "bright_fun_mouse_boil": "Une approbation sera nécessaire pour les nouveaux abonnés.", + "red_vivid_cuckoo_spark": "Autoriser la découverte de compte", + "plain_zany_donkey_dart": "Permettez à votre compte d'être trouvé dans les résultats de recherche.", + "jolly_noble_sloth_breathe": "Mise à jour du profil...", + "tough_alive_niklas_promise": "Aucun changement", + "spry_honest_kestrel_arrive": "Profil mis à jour", + "civil_icy_ant_mend": "La bannière doit être inférieure à {size} octets", + "zippy_caring_raven_edit": "L'avatar doit être inférieur à {size} octets", + "still_upper_otter_dine": "Le nom d'utilisateur ne peut contenir que des lettres minuscules, des chiffres, des traits de soulignement et des tirets", + "aware_house_dolphin_win": "Doit comporter au moins 3 caractères", + "weary_fresh_dragonfly_bless": "Doit être une adresse e-mail valide", + "sunny_novel_otter_glow": "Doit comporter au moins 3 caractères", + "fluffy_soft_wolf_cook": "Email (ou nom d'utilisateur)", + "livid_bright_wallaby_quiz": "Mot de passe", + "fuzzy_sea_moth_absorb": "Se connecter", + "tidy_tidy_cow_cut": "Ou continuer avec", + "slow_these_kestrel_sail": "Accepter", + "weary_steep_yak_embrace": "Rejeter", + "cool_slimy_coyote_affirm": "Acceptation de la demande de suivi...", + "busy_awful_mouse_jump": "Demande de suivi acceptée.", + "front_sunny_penguin_flip": "Rejet de la demande de suivi...", + "green_flat_mayfly_trust": "Demande de suivi rejetée.", + "large_vivid_horse_catch": "a reblogué•e" +} diff --git a/package.json b/package.json index a7b0dcb..284c790 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,12 @@ "url": "git+https://github.com/versia-pub/frontend.git" }, "scripts": { - "build": "nuxt build", + "build": "paraglide-js compile --project ./project.inlang --outdir ./paraglide && nuxt build", "dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 bun --bun nuxt dev --https --https.cert config/versia-fe.localhost.pem --https.key config/versia-fe.localhost-key.pem --host versia-fe.localhost", "generate": "nuxt generate", "emojis:generate": "bun run utils/emojis.ts", - "postinstall": "nuxt prepare", + "postinstall": "paraglide-js compile --project ./project.inlang --outdir ./paraglide && nuxt prepare", + "rebuild-i18n": "paraglide-js compile --project ./project.inlang --outdir ./paraglide", "lint": "bunx @biomejs/biome check .", "check": "bunx tsc -p ." }, @@ -69,7 +70,8 @@ "@tailwindcss/forms": "^0.5.9", "@types/html-to-text": "^9.0.4", "typescript": "^5.7.2", - "vue-tsc": "^2.1.10" + "vue-tsc": "^2.1.10", + "@inlang/paraglide-js": "1.11.3" }, "trustedDependencies": [ "@biomejs/biome", diff --git a/project.inlang/.gitignore b/project.inlang/.gitignore new file mode 100644 index 0000000..5e46596 --- /dev/null +++ b/project.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/project.inlang/project_id b/project.inlang/project_id new file mode 100644 index 0000000..70d5486 --- /dev/null +++ b/project.inlang/project_id @@ -0,0 +1 @@ +9d8c2839bac9b12c091cd75e30f73bf4570b61f052e0bae7af076988269f44ed \ No newline at end of file diff --git a/project.inlang/settings.json b/project.inlang/settings.json new file mode 100644 index 0000000..16555ec --- /dev/null +++ b/project.inlang/settings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "sourceLanguageTag": "en", + "languageTags": ["en", "fr"], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{languageTag}.json" + } +} diff --git a/tsconfig.json b/tsconfig.json index aff85fe..28eea3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "allowImportingTsExtensions": true + "allowImportingTsExtensions": true, + "allowJs": true } }