Skip to content

Commit

Permalink
feat: add setFallbackLocale for i18n closes #4872
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Oct 19, 2024
1 parent ecb540a commit 193a96f
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/pink-poets-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vee-validate/i18n": patch
---

feat: add setFallbackLocale for i18n closes #4872
28 changes: 24 additions & 4 deletions packages/i18n/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type RootI18nDictionary = Record<string, PartialI18nDictionary>;

class Dictionary {
public locale: string;
public fallbackLocale?: string;

private container: RootI18nDictionary;
private interpolateOptions: InterpolateOptions;
Expand All @@ -33,7 +34,19 @@ class Dictionary {
}

public resolve(ctx: FieldValidationMetaInfo, interpolateOptions?: InterpolateOptions) {
return this.format(this.locale, ctx, interpolateOptions);
let result = this.format(this.locale, ctx, interpolateOptions);
if (!result && this.fallbackLocale && this.fallbackLocale !== this.locale) {
result = this.format(this.fallbackLocale, ctx, interpolateOptions);
}

return result || this.getDefaultMessage(this.locale, ctx);
}

public getDefaultMessage(locale: string, ctx: FieldValidationMetaInfo) {
const { label, name } = ctx;
const fieldName = this.resolveLabel(locale, name, label);

return `${fieldName} is not valid`;
}

public getLocaleDefault(locale: string, field: string): string | ValidationMessageGenerator | undefined {
Expand All @@ -54,7 +67,7 @@ class Dictionary {
const fieldName = this.resolveLabel(locale, name, label);

if (!rule) {
message = this.getLocaleDefault(locale, name) || `${fieldName} is not valid`;
message = this.getLocaleDefault(locale, name) || '';
return isCallable(message)
? message(ctx)
: interpolate(message, { ...form, field: fieldName }, interpolateOptions ?? this.interpolateOptions);
Expand All @@ -63,7 +76,7 @@ class Dictionary {
// find if specific message for that field was specified.
message = this.container[locale]?.fields?.[name]?.[rule.name] || this.container[locale]?.messages?.[rule.name];
if (!message) {
message = this.getLocaleDefault(locale, name) || `${fieldName} is not valid`;
message = this.getLocaleDefault(locale, name) || '';
}

return isCallable(message)
Expand Down Expand Up @@ -121,6 +134,13 @@ function setLocale(locale: string) {
DICTIONARY.locale = locale;
}

/**
* Sets the fallback locale.
*/
function setFallbackLocale(locale: string) {
DICTIONARY.fallbackLocale = locale;
}

/**
* Loads a locale file from URL and merges it with the current dictionary
*/
Expand All @@ -143,4 +163,4 @@ async function loadLocaleFromURL(url: string) {
}
}

export { localize, setLocale, loadLocaleFromURL };
export { localize, setLocale, loadLocaleFromURL, setFallbackLocale };
42 changes: 39 additions & 3 deletions packages/i18n/tests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Ref } from 'vue';
import { defineRule, configure, useField } from '@/vee-validate';
import { required, between } from '@/rules';
import { localize, setLocale } from '@/i18n';
import { localize, setFallbackLocale, setLocale } from '@/i18n';
import { mountWithHoc, setValue, flushPromises } from '../../vee-validate/tests/helpers';

defineRule('required', required);
Expand Down Expand Up @@ -550,7 +550,7 @@ describe('interpolation preserves placeholders if not found', () => {
});
});

// begin - #4726 - custom interpolation options
// #4726 - custom interpolation options
test('custom interpolation options - interpolates object params with short format', async () => {
configure({
generateMessage: localize(
Expand Down Expand Up @@ -833,4 +833,40 @@ describe('interpolation preserves placeholders if not found', () => {
expect(error.textContent).toContain('The name field must be between 0 and {{max}}');
});
});
// end - #4726 - custom interpolation options tests

test('can define fallback locale', async () => {
configure({
generateMessage: localize({
en: {
messages: {
test: `Field is required`,
},
},
ar: {
messages: {},
},
}),
});

setLocale('ar');
setFallbackLocale('en');
defineRule('test', () => false);

const wrapper = mountWithHoc({
template: `
<div>
<Field name="name" rules="test" v-slot="{ field, errors }">
<input v-bind="field" type="text">
<span id="error">{{ errors[0] }}</span>
</Field>
</div>
`,
});

const error = wrapper.$el.querySelector('#error');
const input = wrapper.$el.querySelector('input');
setValue(input, '12');
await flushPromises();

expect(error.textContent).toContain('Field is required');
});

0 comments on commit 193a96f

Please # to comment.