From dda469d10e38a17007a4095413d0668aded26669 Mon Sep 17 00:00:00 2001
From: Prospector <6166773+Prospector@users.noreply.github.com>
Date: Mon, 29 Jan 2024 18:55:54 -0800
Subject: [PATCH] Add translations for the default page layout (#1616)
* Convert default.vue to composition API
* Add translations to default page layout
* Generate index.json
* Fix sign up page
* Generate index.json
* Fix atUserLabel
* Fix composition API port
* Follow-up fixes for #1616 (#1618)
* Re-organize default layout messages
- Group some of the messages by their appearance (like banners)
Grouping makes avoids having to think too much about the property
names, keeps declarations clean, and also can be minified better,
since variable names, unlike property names, can be easily mangled.
- Remove needless type specification in property name[^1]
It's already clear from where the message is formatted what it does,
and if you need clarification, you can Ctrl+hover and see the type in
the key.
- Change some keys to conform to conventions
We generally follow the pattern location > kind?. Things like 'label'
are unnecessary, as everything is assumed to be label by default; the
same applies to things like 'button', which are also often unnecessary
as they're part of the location compound.
- Remove message for handle, which should not be translatable
It's very unlikely user handle format changes between any of the
locales.
[^1]: Common messages are pending restructuring that would fix this
* Fix navRoutes not being computed in default layout
* Fix untranslated Get Modrinth App buttons in default layout
* Make legal disclaimer in the default layout translatable
It wouldn't make much sense to leave it untranslated since it is meant
for the end users too. It is also so small that it's unlikely to be
mistranslated.
* Extract missed legal disclaimer message from default layout
* Make SEO strings in default layout translatable
---------
Co-authored-by: Sasha Sorokin <10401817+brawaru@users.noreply.github.com>
---
layouts/default.vue | 498 ++++++++++++------
locales/en-US/index.json | 111 +++-
pages/auth/sign-in.vue | 8 +-
pages/auth/sign-up.vue | 6 +-
pages/dashboard/projects.vue | 6 +-
pages/organization/[id]/settings/projects.vue | 4 +-
utils/common-messages.ts | 28 +
7 files changed, 469 insertions(+), 192 deletions(-)
diff --git a/layouts/default.vue b/layouts/default.vue
index 236c58ae6..b346c9e80 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -5,14 +5,16 @@
class="email-nag"
>
- For security purposes, please verify your email address on Modrinth.
- Re-send verification email
+ {{ formatMessage(verifyEmailBannerMessages.title) }}
+
+ {{ formatMessage(verifyEmailBannerMessages.action) }}
+
- For security purposes, please enter your email on Modrinth.
+ {{ formatMessage(addEmailBannerMessages.title) }}
- Visit account settings
+ {{ formatMessage(addEmailBannerMessages.action) }}
@@ -25,12 +27,10 @@
>
- You’re viewing Modrinth’s staging environment
+ {{ formatMessage(stagingBannerMessages.title) }}
- The staging environment is running on a copy of the production Modrinth database. This is
- used for testing and debugging purposes, and may be running in-development versions of the
- Modrinth backend or frontend newer than the production instance.
+ {{ formatMessage(stagingBannerMessages.description) }}
@@ -53,13 +53,13 @@
v-if="auth.user"
to="/dashboard/notifications"
class="control-button button-transparent"
- title="Notifications"
+ :title="formatMessage(commonMessages.notificationsLabel)"
>
@@ -78,7 +78,7 @@
@@ -88,13 +88,15 @@
@{{ auth.user.username }}
-
Visit your profile
+
{{ formatMessage(messages.visitYourProfile) }}
- Create a project
+
+ {{ formatMessage(commonMessages.createAProjectButton) }}
+
@@ -103,15 +105,17 @@
- Notifications
+ {{
+ formatMessage(commonMessages.notificationsLabel)
+ }}
- Dashboard
+ {{ formatMessage(commonMessages.dashboardLabel) }}
- Settings
+ {{ formatMessage(commonMessages.settingsLabel) }}
- Moderation
+ {{ formatMessage(commonMessages.moderationLabel) }}
- Get Modrinth App
+
+ {{ formatMessage(messages.getModrinthApp) }}
+
- Log out
+
+ {{ formatMessage(commonMessages.signOutButton) }}
+
- Sign in
+ {{ formatMessage(commonMessages.signInButton) }}
- Get Modrinth App
+ {{ formatMessage(messages.getModrinthApp) }}
@@ -185,32 +193,32 @@
@{{ auth.user.username }}
-
Visit your profile
+
{{ formatMessage(messages.visitYourProfile) }}
- Sign in
+ {{ formatMessage(commonMessages.signInButton) }}
- Log out
+ {{ formatMessage(commonMessages.signOutButton) }}
- Create a project
+ {{ formatMessage(commonMessages.createAProjectButton) }}
- Collections
+ {{ formatMessage(commonMessages.collectionsLabel) }}
- Moderation
+ {{ formatMessage(commonMessages.moderationLabel) }}
- Settings
+ {{ formatMessage(commonMessages.settingsLabel) }}
- Change theme
+
+ {{ formatMessage(messages.changeTheme) }}
+
-
+
@@ -247,7 +261,7 @@
- Search
+ {{ formatMessage(navMenuMessages.search) }}
@@ -257,7 +271,7 @@
:class="{
'no-active': isMobileMenuOpen || isBrowseMenuOpen,
}"
- title="Notifications"
+ :title="formatMessage(commonMessages.notificationsLabel)"
@click="
() => {
isMobileMenuOpen = false
@@ -267,13 +281,17 @@
>
-
+
@@ -285,7 +303,7 @@
:src="auth.user.avatar_url"
class="user-icon"
:class="{ expanded: isMobileMenuOpen }"
- alt="Your avatar"
+ :alt="formatMessage(messages.yourAvatarAlt)"
aria-hidden="true"
circle
/>
@@ -300,17 +318,24 @@
-
Interact
+
{{ formatMessage(footerMessages.interactTitle) }}
Discord
X (Twitter)
Mastodon
@@ -358,20 +392,20 @@
- Get Modrinth App
+ {{ formatMessage(messages.getModrinthApp) }}
- Change theme
+ {{ formatMessage(messages.changeTheme) }}
- Settings
+ {{ formatMessage(commonMessages.settingsLabel) }}
- NOT AN OFFICIAL MINECRAFT PRODUCT. NOT APPROVED BY OR ASSOCIATED WITH MOJANG.
+ {{ formatMessage(footerMessages.legalDisclaimer) }}
@@ -397,6 +431,8 @@ import ChartIcon from '~/assets/images/utils/chart.svg'
import NavRow from '~/components/ui/NavRow.vue'
import ModalCreation from '~/components/ui/ModalCreation.vue'
import Avatar from '~/components/ui/Avatar.vue'
+import { getProjectTypeMessage } from '~/utils/i18n-project-type.ts'
+import { commonMessages } from '~/utils/common-messages'
const { formatMessage } = useVIntl()
@@ -408,6 +444,131 @@ const tags = useTags()
const config = useRuntimeConfig()
const route = useRoute()
const link = config.public.siteUrl + route.path.replace(/\/+$/, '')
+
+const verifyEmailBannerMessages = defineMessages({
+ title: {
+ id: 'layout.banner.verify-email.title',
+ defaultMessage: 'For security purposes, please verify your email address on Modrinth.',
+ },
+ action: {
+ id: 'layout.banner.verify-email.action',
+ defaultMessage: 'Re-send verification email',
+ },
+})
+
+const addEmailBannerMessages = defineMessages({
+ title: {
+ id: 'layout.banner.add-email.title',
+ defaultMessage: 'For security purposes, please enter your email on Modrinth.',
+ },
+ action: {
+ id: 'layout.banner.add-email.button',
+ defaultMessage: 'Visit account settings',
+ },
+})
+
+const stagingBannerMessages = defineMessages({
+ title: {
+ id: 'layout.banner.staging.title',
+ defaultMessage: 'You’re viewing Modrinth’s staging environment.',
+ },
+ description: {
+ id: 'layout.banner.staging.description',
+ defaultMessage:
+ 'The staging environment is running on a copy of the production Modrinth database. This is used for testing and debugging purposes, and may be running in-development versions of the Modrinth backend or frontend newer than the production instance.',
+ },
+})
+
+const navMenuMessages = defineMessages({
+ home: {
+ id: 'layout.nav.home',
+ defaultMessage: 'Home',
+ },
+ search: {
+ id: 'layout.nav.search',
+ defaultMessage: 'Search',
+ },
+})
+
+const messages = defineMessages({
+ visitYourProfile: {
+ id: 'layout.label.visit-your-profile',
+ defaultMessage: 'Visit your profile',
+ },
+ toggleMenu: {
+ id: 'layout.menu-toggle.action',
+ defaultMessage: 'Toggle menu',
+ },
+ yourAvatarAlt: {
+ id: 'layout.avatar.alt',
+ defaultMessage: 'Your avatar',
+ },
+ getModrinthApp: {
+ id: 'layout.action.get-modrinth-app',
+ defaultMessage: 'Get Modrinth App',
+ },
+ changeTheme: {
+ id: 'layout.action.change-theme',
+ defaultMessage: 'Change theme',
+ },
+})
+
+const footerMessages = defineMessages({
+ openSource: {
+ id: 'layout.footer.open-source',
+ defaultMessage: 'Modrinth is open source .',
+ },
+ companyTitle: {
+ id: 'layout.footer.company.title',
+ defaultMessage: 'Company',
+ },
+ terms: {
+ id: 'layout.footer.company.terms',
+ defaultMessage: 'Terms',
+ },
+ privacy: {
+ id: 'layout.footer.company.privacy',
+ defaultMessage: 'Privacy',
+ },
+ rules: {
+ id: 'layout.footer.company.rules',
+ defaultMessage: 'Rules',
+ },
+ careers: {
+ id: 'layout.footer.company.careers',
+ defaultMessage: 'Careers',
+ },
+ resourcesTitle: {
+ id: 'layout.footer.resources.title',
+ defaultMessage: 'Resources',
+ },
+ support: {
+ id: 'layout.footer.resources.support',
+ defaultMessage: 'Support',
+ },
+ blog: {
+ id: 'layout.footer.resources.blog',
+ defaultMessage: 'Blog',
+ },
+ docs: {
+ id: 'layout.footer.resources.docs',
+ defaultMessage: 'Docs',
+ },
+ status: {
+ id: 'layout.footer.resources.status',
+ defaultMessage: 'Status',
+ },
+ interactTitle: {
+ id: 'layout.footer.interact.title',
+ defaultMessage: 'Interact',
+ },
+ legalDisclaimer: {
+ id: 'layout.footer.legal-disclaimer',
+ defaultMessage:
+ 'NOT AN OFFICIAL MINECRAFT SERVICE. NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.',
+ },
+})
+
useHead({
link: [
{
@@ -416,14 +577,15 @@ useHead({
},
],
})
-
-const description =
- 'Download Minecraft mods, plugins, datapacks, shaders, resourcepacks, and modpacks on Modrinth. ' +
- 'Discover and publish projects on Modrinth with a modern, easy to use interface and API.'
-
useSeoMeta({
title: 'Modrinth',
- description,
+ description: () =>
+ formatMessage({
+ id: 'layout.meta.description',
+ defaultMessage:
+ 'Download Minecraft mods, plugins, datapacks, shaders, resourcepacks, and modpacks on Modrinth. ' +
+ 'Discover and publish projects on Modrinth with a modern, easy to use interface and API.',
+ }),
publisher: 'Modrinth',
themeColor: '#1bd96a',
colorScheme: 'dark light',
@@ -431,7 +593,11 @@ useSeoMeta({
// OpenGraph
ogTitle: 'Modrinth',
ogSiteName: 'Modrinth',
- ogDescription: 'Discover and publish Minecraft content!',
+ ogDescription: () =>
+ formatMessage({
+ id: 'layout.meta.og-description',
+ defaultMessage: 'Discover and publish Minecraft content!',
+ }),
ogType: 'website',
ogImage: 'https://cdn.modrinth.com/modrinth-new.png',
ogUrl: link,
@@ -441,12 +607,67 @@ useSeoMeta({
twitterSite: '@modrinth',
})
-let developerModeCounter = 0
+const developerModeCounter = ref(0)
+
+const isDropdownOpen = ref(false)
+const isMobileMenuOpen = ref(false)
+const isBrowseMenuOpen = ref(false)
+const navRoutes = computed(() => [
+ {
+ label: formatMessage(getProjectTypeMessage('mod', true)),
+ href: '/mods',
+ },
+ {
+ label: formatMessage(getProjectTypeMessage('plugin', true)),
+ href: '/plugins',
+ },
+ {
+ label: formatMessage(getProjectTypeMessage('datapack', true)),
+ href: '/datapacks',
+ },
+ {
+ label: formatMessage(getProjectTypeMessage('shader', true)),
+ href: '/shaders',
+ },
+ {
+ label: formatMessage(getProjectTypeMessage('resourcepack', true)),
+ href: '/resourcepacks',
+ },
+ {
+ label: formatMessage(getProjectTypeMessage('modpack', true)),
+ href: '/modpacks',
+ },
+])
+
+onMounted(() => {
+ if (window && process.client) {
+ window.history.scrollRestoration = 'auto'
+ }
+
+ runAnalytics()
+})
+
+watch(
+ () => route.path,
+ () => {
+ isMobileMenuOpen.value = false
+ isBrowseMenuOpen.value = false
+
+ if (process.client) {
+ document.body.style.overflowY = 'scroll'
+ document.body.setAttribute('tabindex', '-1')
+ document.body.removeAttribute('tabindex')
+ }
+
+ updateCurrentDate()
+ runAnalytics()
+ }
+)
function developerModeIncrement() {
- if (developerModeCounter >= 5) {
+ if (developerModeCounter.value >= 5) {
cosmetics.value.developerMode = !cosmetics.value.developerMode
- developerModeCounter = 0
+ developerModeCounter.value = 0
if (cosmetics.value.developerMode) {
app.$notify({
group: 'main',
@@ -463,7 +684,7 @@ function developerModeIncrement() {
})
}
} else {
- developerModeCounter++
+ developerModeCounter.value++
}
}
@@ -471,110 +692,43 @@ async function logoutUser() {
await logout()
}
+function runAnalytics() {
+ const config = useRuntimeConfig()
+ const replacedUrl = config.public.apiBaseUrl.replace('v2/', '')
+
+ setTimeout(() => {
+ $fetch(`${replacedUrl}analytics/view`, {
+ method: 'POST',
+ body: {
+ url: window.location.href,
+ },
+ })
+ .then(() => {})
+ .catch(() => {})
+ })
+}
+function toggleMobileMenu() {
+ isMobileMenuOpen.value = !isMobileMenuOpen.value
+ if (isMobileMenuOpen.value) {
+ isBrowseMenuOpen.value = false
+ }
+}
+function toggleBrowseMenu() {
+ isBrowseMenuOpen.value = !isBrowseMenuOpen.value
+
+ if (isBrowseMenuOpen.value) {
+ isMobileMenuOpen.value = false
+ }
+}
+function changeTheme() {
+ updateTheme(app.$colorMode.value === 'dark' ? 'light' : 'dark', true)
+}
+
function hideStagingBanner() {
cosmetics.value.hideStagingBanner = true
saveCosmetics()
}
-