From 4b7fab3c0541160c8572bc14739b5ca2d2185a81 Mon Sep 17 00:00:00 2001 From: Ivan Demchuk Date: Thu, 4 Apr 2024 17:22:06 +0300 Subject: [PATCH 1/2] Add support for SSR props in Vue 3 directive --- src/vue/directive.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/vue/directive.ts b/src/vue/directive.ts index d1586879..24455fdf 100644 --- a/src/vue/directive.ts +++ b/src/vue/directive.ts @@ -23,6 +23,8 @@ function translate(el: HTMLElement, fluent: TranslationContext, binding: VueDire for (const [attr, attrValue] of Object.entries(translation.attributes)) { if (isAttrNameLocalizable(attr, el, allowedAttrs)) el.setAttribute(attr, attrValue) + else + warn(`Attribute '${attr}' on element <${el.tagName.toLowerCase()}> is not localizable. Remove it from the translation. Translation key: ${key}`) } } @@ -39,6 +41,30 @@ export function createVue3Directive(rootContext: TranslationContext): Vue3Direct const context = getContext(rootContext, binding.instance) translate(el, context, binding) }, + + getSSRProps(binding) { + const context = getContext(rootContext, binding.instance) + const key = binding.arg + if (key === void 0) { + warn('v-t directive is missing arg with translation key') + return {} + } + const translation = context.formatWithAttrs(key, binding.value) + const allowedAttrs = Object.keys(binding.modifiers) + const attrs: Record = {} + for (const [attr, attrValue] of Object.entries(translation.attributes)) { + // Vue 3 does not expose the element in the binding object + // so we can't check if the attribute is allowed + // we assume that all attributes are allowed + // this could lead to SSR hydration mismatches if translation + // contains attributes that are not allowed + // There is a runtime warning in the browser console in case translation contains not allowed attributes + if (isAttrNameLocalizable(attr, {} as HTMLElement, allowedAttrs)) + attrs[attr] = attrValue + } + + return attrs + }, } } From f3189bd0aa5c85c74fd0d8fb6a625cb370e653da Mon Sep 17 00:00:00 2001 From: Ivan Demchuk Date: Thu, 4 Apr 2024 17:55:20 +0300 Subject: [PATCH 2/2] Add tests for SSR rendering of v-t directive --- __tests__/utils/ssr.ts | 22 +++++++++++++++ __tests__/vue/directive.spec.ts | 6 +++++ __tests__/vue/ssr.spec.ts | 48 +++++++++++++++++++++++++++++++++ src/vue/directive.ts | 1 + 4 files changed, 77 insertions(+) create mode 100644 __tests__/utils/ssr.ts create mode 100644 __tests__/vue/ssr.spec.ts diff --git a/__tests__/utils/ssr.ts b/__tests__/utils/ssr.ts new file mode 100644 index 00000000..e4df44db --- /dev/null +++ b/__tests__/utils/ssr.ts @@ -0,0 +1,22 @@ +import * as TestUtils from '@vue/test-utils' +import type { ComponentOptions } from 'vue-3' +import { install } from 'vue-demi' + +import type { FluentVue } from '../../src' + +install() + +export function renderSSR( + fluent: FluentVue | null, + component: ComponentOptions, +): Promise { + const { renderToString } = TestUtils + + const plugins = fluent ? [fluent] : [] + + return renderToString(component, { + global: { + plugins, + }, + }) +} diff --git a/__tests__/vue/directive.spec.ts b/__tests__/vue/directive.spec.ts index f0c5db4e..2bbf1e3f 100644 --- a/__tests__/vue/directive.spec.ts +++ b/__tests__/vue/directive.spec.ts @@ -141,6 +141,8 @@ describe('directive', () => { `), ) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const component = { data: () => ({ name: 'John', @@ -153,6 +155,10 @@ describe('directive', () => { // Assert expect(mounted.html()).toEqual('Text') + expect(warn).toHaveBeenCalledTimes(1) + expect(warn).toHaveBeenCalledWith( + '[fluent-vue] Attribute \'not-allowed\' on element is not localizable. Remove it from the translation. Translation key: link', + ) }) it('works without fallbacks', () => { diff --git a/__tests__/vue/ssr.spec.ts b/__tests__/vue/ssr.spec.ts new file mode 100644 index 00000000..55ae8988 --- /dev/null +++ b/__tests__/vue/ssr.spec.ts @@ -0,0 +1,48 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { isVue3 } from 'vue-demi' + +import { FluentBundle, FluentResource } from '@fluent/bundle' +import ftl from '@fluent/dedent' + +import { renderSSR } from '../utils/ssr' + +import type { FluentVue } from '../../src' +import { createFluentVue } from '../../src' + +describe.skipIf(!isVue3)('sSR directive', () => { + let fluent: FluentVue + let bundle: FluentBundle + + beforeEach(() => { + bundle = new FluentBundle('en-US') + + fluent = createFluentVue({ + bundles: [bundle], + }) + }) + + it('translates text content', async () => { + // Arrange + bundle.addResource( + new FluentResource(ftl` + link = Link text + .aria-label = Link aria label + `), + ) + + const component = { + data: () => ({ + name: 'John', + }), + template: 'Fallback text', + } + + // Act + const rendered = await renderSSR(fluent, component) + + // Assert + // This has fallback text because the textContent is not supported by Vue getSSRProps + // Text will be translated using directive transform + expect(rendered).toEqual('Fallback text') + }) +}) diff --git a/src/vue/directive.ts b/src/vue/directive.ts index 24455fdf..23716fe6 100644 --- a/src/vue/directive.ts +++ b/src/vue/directive.ts @@ -63,6 +63,7 @@ export function createVue3Directive(rootContext: TranslationContext): Vue3Direct attrs[attr] = attrValue } + // TODO: Include textContent when https://github.com/vuejs/core/issues/8112 is resolved return attrs }, } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy