Skip to content

Commit 699838f

Browse files
committed
feat(plugin): allow passing multiple bundles to plugin
When message is not found in first bundle try to find is using next bundle BREAKING CHANGE: Instead on `bundle` option plugin now accepts `bundles` option with array of bundles
1 parent 80757f7 commit 699838f

File tree

11 files changed

+163
-30
lines changed

11 files changed

+163
-30
lines changed

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@
6464
"coverageThreshold": {
6565
"global": {
6666
"branches": 85,
67-
"functions": 95,
68-
"lines": 95,
69-
"statements": 95
67+
"functions": 85,
68+
"lines": 85,
69+
"statements": 85
7070
}
7171
},
7272
"collectCoverageFrom": [
@@ -130,6 +130,8 @@
130130
},
131131
"dependencies": {
132132
"@fluent/bundle": "^0.14.0",
133-
"@fluent/dedent": "^0.1.0"
133+
"@fluent/dedent": "^0.1.0",
134+
"@fluent/sequence": "^0.4.0",
135+
"cached-iterable": "^0.3.0"
134136
}
135137
}

src/directive.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,26 @@ import { warn } from './util/warn'
44
import { FluentVueObject } from '../types'
55

66
function translate(el: HTMLElement, fluent: FluentVueObject, binding: DirectiveBinding) {
7-
if (binding.arg === undefined) {
7+
const key = binding.arg
8+
9+
if (key === undefined) {
810
warn(false, 'v-t directive is missing arg with translation key')
911
return
1012
}
1113

12-
const msg = fluent.getMessage(binding.arg)
14+
const bundle = fluent.getBundle(key)
15+
const msg = fluent.getMessage(bundle, key)
1316

14-
if (msg === undefined) {
17+
if (bundle === null || msg === null) {
1518
return
1619
}
1720

1821
if (msg.value !== null) {
19-
el.textContent = fluent.formatPattern(msg.value, binding.value)
22+
el.textContent = fluent.formatPattern(bundle, msg.value, binding.value)
2023
}
2124

2225
for (const [attr] of Object.entries(binding.modifiers)) {
23-
el.setAttribute(attr, fluent.formatPattern(msg.attributes[attr], binding.value))
26+
el.setAttribute(attr, fluent.formatPattern(bundle, msg.attributes[attr], binding.value))
2427
}
2528
}
2629

src/fluent-vue.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,54 @@
11
import { FluentVueObject, FluentVueOptions } from '../types'
22
import { Vue, VueConstructor } from 'vue/types/vue'
33
import { warn } from './util/warn'
4-
import { Pattern } from '@fluent/bundle'
4+
import { Pattern, FluentBundle } from '@fluent/bundle'
5+
6+
import { CachedSyncIterable } from 'cached-iterable'
7+
import { mapBundleSync } from '@fluent/sequence'
58

69
export default class FluentVue implements FluentVueObject {
7-
private options: FluentVueOptions
10+
bundles: CachedSyncIterable
11+
812
static install: (vue: VueConstructor<Vue>) => void
913

1014
constructor(options: FluentVueOptions) {
11-
this.options = options
15+
this.bundles = CachedSyncIterable.from(options.bundles)
16+
}
17+
18+
getBundle(key: string): FluentBundle {
19+
return mapBundleSync(this.bundles, key)
1220
}
1321

14-
getMessage(key: string) {
15-
const message = this.options.bundle.getMessage(key)
22+
getMessage(bundle: FluentBundle | null, key: string) {
23+
if (bundle === null) {
24+
warn(false, `Could not find translation for key [${key}]`)
25+
return null
26+
}
27+
28+
const message = bundle.getMessage(key)
1629

1730
if (message === undefined) {
1831
warn(false, `Could not find translation for key [${key}]`)
19-
return undefined
32+
return null
2033
}
2134

2235
return message
2336
}
2437

25-
formatPattern(message: Pattern, value?: object, errors?: string[]): string {
26-
return this.options.bundle.formatPattern(message, value, errors)
38+
formatPattern(bundle: FluentBundle, message: Pattern, value?: object, errors?: string[]): string {
39+
return bundle.formatPattern(message, value, errors)
2740
}
2841

2942
format(key: string, value?: object): string {
30-
const message = this.getMessage(key)
43+
const context = this.getBundle(key)
44+
const message = this.getMessage(context, key)
3145

32-
if (message === undefined || message.value === null) {
46+
if (message === null || message.value === null) {
3347
return key
3448
}
3549

3650
const errors: string[] = []
37-
const result = this.formatPattern(message.value, value, errors)
51+
const result = this.formatPattern(context, message.value, value, errors)
3852
for (const error of errors) {
3953
warn(false, `Fluent error for key [${key}]: ${error}`)
4054
}

src/types/fluent.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ declare module '@fluent/dedent' {
22
export default function ftl(strings: TemplateStringsArray): string
33
}
44

5+
declare module 'cached-iterable' {
6+
export class CachedSyncIterable {
7+
static from(array: any[]): CachedSyncIterable
8+
}
9+
}
10+
11+
declare module '@fluent/sequence' {
12+
import { CachedSyncIterable } from 'cached-iterable'
13+
import { FluentBundle } from '@fluent/bundle'
14+
15+
export function mapBundleSync(iterable: CachedSyncIterable, key: string): FluentBundle
16+
}
17+
518
declare module '@fluent/bundle' {
619
export interface FluentBundleContructorOptions {
720
functions?: object
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`language change can work with multiple bundles 1`] = `<a href="/foo">текст посилання</a>`;
4+
5+
exports[`language change falls back to previous bundle 1`] = `<a href="/foo">link text</a>`;

test/change-language.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { createLocalVue, mount } from '@vue/test-utils'
2+
3+
import { FluentBundle, FluentResource } from '@fluent/bundle'
4+
import ftl from '@fluent/dedent'
5+
6+
import FluentVue from '../src'
7+
8+
describe('language change', () => {
9+
let options: any
10+
let bundleEn: FluentBundle
11+
let bundleUk: FluentBundle
12+
13+
beforeEach(() => {
14+
const localVue = createLocalVue()
15+
localVue.use(FluentVue)
16+
17+
bundleEn = new FluentBundle('en-US', {
18+
useIsolating: false
19+
})
20+
21+
bundleUk = new FluentBundle('uk-UA', {
22+
useIsolating: false
23+
})
24+
25+
const fluent = new FluentVue({
26+
bundles: [bundleUk, bundleEn]
27+
})
28+
29+
options = {
30+
fluent,
31+
localVue
32+
}
33+
})
34+
35+
it('can work with multiple bundles', () => {
36+
// Arrange
37+
bundleEn.addResource(
38+
new FluentResource(ftl`
39+
link = link text
40+
`)
41+
)
42+
43+
bundleUk.addResource(
44+
new FluentResource(ftl`
45+
link = текст посилання
46+
`)
47+
)
48+
49+
const component = {
50+
template: `<a v-t:link href="/foo">Fallback text</a>`
51+
}
52+
53+
// Act
54+
const mounted = mount(component, options)
55+
56+
// Assert
57+
expect(mounted).toMatchSnapshot()
58+
})
59+
60+
it('falls back to previous bundle', () => {
61+
// Arrange
62+
bundleEn.addResource(
63+
new FluentResource(ftl`
64+
link = link text
65+
`)
66+
)
67+
68+
const component = {
69+
template: `<a v-t:link href="/foo">Fallback text</a>`
70+
}
71+
72+
// Act
73+
const mounted = mount(component, options)
74+
75+
// Assert
76+
expect(mounted).toMatchSnapshot()
77+
})
78+
})

test/directive.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('directive', () => {
1818
})
1919

2020
const fluent = new FluentVue({
21-
bundle
21+
bundles: [bundle]
2222
})
2323

2424
options = {

test/error-handling.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('vue integration', () => {
2121
)
2222

2323
const fluent = new FluentVue({
24-
bundle
24+
bundles: [bundle]
2525
})
2626

2727
const options = {

test/vue-integration.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('vue integration', () => {
2121
)
2222

2323
const fluent = new FluentVue({
24-
bundle
24+
bundles: [bundle]
2525
})
2626

2727
const options = {

types/index.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { Vue } from 'vue/types/vue'
22
import { FluentBundle, MessageInfo, Pattern } from '@fluent/bundle'
33

44
export interface FluentVueObject {
5-
getMessage(key: string): MessageInfo | undefined
6-
formatPattern(message: Pattern, value?: object, errors?: string[]): string
5+
getBundle(key: string): FluentBundle | null
6+
getMessage(bundle: FluentBundle | null, key: string): MessageInfo | null
7+
formatPattern(bundle: FluentBundle, message: Pattern, value?: object, errors?: string[]): string
78
format(key: string, value?: object): string
89
}
910

1011
export interface FluentVueOptions {
11-
bundle: FluentBundle
12+
bundles: FluentBundle[]
1213
}
1314

1415
declare module 'vue/types/vue' {

0 commit comments

Comments
 (0)