Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Add migration notice and translation banner to the blank layout; fix translation banner logic #904

Merged
merged 7 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 13 additions & 95 deletions src/composables/use-i18n-sync.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,34 @@
import { sendWindowMessage } from '~/utils/send-message.js'
import untranslatedLocales from '../locales/scripts/untranslated-locales.json'
import {
computed,
onMounted,
onUnmounted,
reactive,
ref,
useContext,
} from '@nuxtjs/composition-api'
import { computed, reactive, useContext } from '@nuxtjs/composition-api'
import { StorageSerializers, useStorage } from '~/composables/use-storage'

const BASE_URL = 'https://translate.wordpress.org/projects/meta/openverse/'

export default function useI18nSync() {
const { i18n } = useContext()
const { app } = useContext()

const currentLocale = computed(() => {
return i18n.locales.find((item) => item.code === i18n.locale)
return app.i18n.locales.find((item) => item.code === app.i18n.locale)
})
const showBanner = ref(false)

const bannerLocale = reactive({
code: i18n.locale,
code: app.i18n.locale,
name: currentLocale.value.name,
})

/**
* Updates the i18n locale code and sets the html lang and dir attributes.
*
* @param {Object} [locale]
* @param {string} locale.code - the locale code as used in i18n.
* @param {string} locale.lang - value of the HTML lang property.
* @param {'rtl'|'ltr'} locale.dir - text direction.
* @returns {Promise<void>}
*/
const updateLocale = async (
locale = {
code: 'en',
lang: 'en-US',
dir: 'ltr',
}
) => {
await i18n.setLocale(locale.code)
document.documentElement.lang = locale.lang
// Always set `dir` property, default to 'ltr'
document.documentElement.dir = locale.dir
}
/**
* Show banner inviting to contribute translations if fewer than 90%
* of strings are translated for current locale.
* Hard-coded to false for default locales: `en` and `en_US`.
*
* @param {import('../locales/scripts/types').i18nLocaleProps} locale - current locale object
* @param {string} localeCode - the slug for the current locale. This is the same as the locale `code`.
* @returns {boolean}
*/
const needsTranslationBanner = (locale) => {
if (['en', 'en_US'].includes(locale)) return false
const needsTranslationBanner = (localeCode) => {
if (['en'].includes(localeCode)) return false

let locale = app.i18n.locales.find((item) => item.code === localeCode)

return locale.translated <= 90
return !(locale?.translated && locale.translated > 90)
}

const bannerDismissedForLocales = useStorage(
Expand All @@ -68,8 +40,8 @@ export default function useI18nSync() {
)
const shouldHideBanner = computed(() => {
return (
!showBanner.value ||
bannerDismissedForLocales.value.includes(bannerLocale.code)
bannerDismissedForLocales.value.includes(bannerLocale.code) ||
!needsTranslationBanner(bannerLocale.code)
)
})
const dismissBanner = () => {
Expand All @@ -81,61 +53,7 @@ export default function useI18nSync() {
const translationLink = computed(
() => `${BASE_URL}${bannerLocale.code}/default/`
)
/**
* Handles messages of type `localeSet` received by the `iframe`. Any
* other message types will be discarded.
*
* It sets the i18n locale property, defaulting to `en` if 0 translations are available for the locale,
* and shows the translation banner if necessary,
*
* @param {string} type - the nature of the message received.
* @param {Object} value - the data sent with the message.
* @param {string} value.locale - wpLocale code of the locale, e.g. 'en_US'.
* @param {string} value.lang - the iso code for language
* (and sometimes country), e.g. 'en-US'.
* Default lang value on wp.org is 'en', and on wp.org/openverse - 'en-US'.
* @param {'rtl'|'ltr'} [value.dir] - the locale text direction.
*/
const localeMsgHandler = async ({ data: { type, value } }) => {
if (type !== 'localeSet') return
// If the locale set by wp.org is 'en_US', not 'en', this is not necessary.
const wpLocaleValue = value.locale === 'en' ? 'en_US' : value.locale
let locale = i18n.locales.find((item) => item.wpLocale === wpLocaleValue)
let htmlLocaleProps
/**
* i18n.locales list only contains the locales that have at least one
* translated string.
*/
if (locale) {
htmlLocaleProps = {
code: locale.code,
lang: locale.wpLocale.replace('_', '-'),
dir: locale.dir === 'rtl' ? 'rtl' : 'ltr',
}
} else {
locale = Object.values(untranslatedLocales).find(
(item) => item.wpLocale === value.locale
)
}
await updateLocale(htmlLocaleProps)

if (needsTranslationBanner(locale)) {
showBanner.value = true
bannerLocale.code = locale.code || null
bannerLocale.name = locale.name || 'this'
}
}

onMounted(() => {
window.addEventListener('message', localeMsgHandler)
sendWindowMessage({
type: 'localeGet',
value: {},
})
})
onUnmounted(() => {
window.removeEventListener('message', localeMsgHandler)
})
return {
shouldHideBanner,
dismissBanner,
Expand Down
18 changes: 16 additions & 2 deletions src/layouts/blank.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
<template>
<Nuxt />
<div>
<VMigrationNotice v-show="isReferredFromCc" />
<VTranslationStatusBanner />
<Nuxt />
</div>
</template>

<script>
import { defineComponent } from '@nuxtjs/composition-api'
import { defineComponent, useStore } from '@nuxtjs/composition-api'
import iframeHeight from '~/mixins/iframe-height'
import { NAV } from '~/constants/store-modules'

import VTranslationStatusBanner from '~/components/VTranslationStatusBanner.vue'
import VMigrationNotice from '~/components/VMigrationNotice.vue'

export default defineComponent({
name: 'Blank',
components: { VMigrationNotice, VTranslationStatusBanner },
mixins: [iframeHeight],
setup() {
const store = useStore()
const isReferredFromCc = store.state[NAV].isReferredFromCc
return { isReferredFromCc }
},
head() {
return this.$nuxtI18nHead({ addSeoAttributes: true, addDirAttribute: true })
},
Expand Down
35 changes: 35 additions & 0 deletions test/e2e/translation-banner.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const { test, expect } = require('@playwright/test')

test('Can see the translation banner and go to the correct link', async ({
page,
}) => {
await page.goto('/ru/search/')
await page.click(
'text=The translation for Russian locale is incomplete. Help us get to 100 percent by '
)

const [page1] = await Promise.all([
page.waitForEvent('popup'),
page.click('text=contributing a translation'),
])
await expect(page1).toHaveURL(
'https://translate.wordpress.org/projects/meta/openverse/ru/default/'
)
})

test('Can close the translation banner', async ({ page }) => {
await page.goto('/ru/search/')
await page.click('[aria-label="Закрыть"]')

const banner = await page.locator(
'.span:has-text("Help us get to 100 percent")'
)
await expect(banner).not.toBeVisible({ timeout: 100 })
// Test that the banner does not re-appear when navigating to the 'About us' page
await page.click('[aria-label="меню"]')
await page.click('a[role="menuitemcheckbox"]:has-text("Наша история")')
await expect(banner).not.toBeVisible({ timeout: 100 })

await page.goto('/ru/search/')
await expect(banner).not.toBeVisible({ timeout: 100 })
})