Skip to content

Commit

Permalink
Fix flow types and enable flow types testing on CI (#896)
Browse files Browse the repository at this point in the history
* Fix broken unit tests

The tests were using "assert" function wrong.
"assert" is defined as: assert(value, [message])

The tests passed two arguments in an attempt to compare them while
the only thing that happened was that the first value was checked for
being truthy.

* Fix flow types and enable flow testing on CI

Types fixed by making "getChoiceIndex" method follow types and be
a class property rather than class method. Class methods are "read-only"
in flow and that wasn't compatible with flow types that are defining
that function as a property (to allow overriding through prototype changing).
  • Loading branch information
rchl committed May 27, 2020
1 parent 8b3c405 commit 092065e
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 48 deletions.
5 changes: 4 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ jobs:
- run:
name: Lint
command: yarn lint
#- run: yarn flow
- run:
name: Flow Types
# https://discuss.circleci.com/t/circleci-terminal-is-a-tty-but-term-is-not-set/9965/8
command: TERM=dumb yarn flow
- run:
name: TS Types
command: yarn test:types
Expand Down
3 changes: 2 additions & 1 deletion decls/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ declare type DateTimeFormatResult = string;
declare type NumberFormatResult = string;
declare type MissingHandler = (locale: Locale, key: Path, vm?: any) => string | void;
declare type PostTranslationHandler = (str: string, key?: string) => string;
declare type GetChoiceIndex = (choice: number, choicesLength: number) => number
declare type ComponentInstanceCreatedListener = (newI18n: I18n, rootI18n: I18n) => void;

declare type FormattedNumberPartType = 'currency' | 'decimal' | 'fraction' | 'group' | 'infinity' | 'integer' | 'literal' | 'minusSign' | 'nan' | 'plusSign' | 'percentSign';
Expand Down Expand Up @@ -144,7 +145,7 @@ declare interface I18n {
setNumberFormat (locale: Locale, format: NumberFormat): void,
mergeNumberFormat (locale: Locale, format: NumberFormat): void,
n (value: number, ...args: any): NumberFormatResult,
getChoiceIndex: (choice: number, choicesLength: number) => number,
getChoiceIndex: GetChoiceIndex,
pluralizationRules: PluralizationRules,
preserveDirectiveContent: boolean
};
Expand Down
65 changes: 36 additions & 29 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export default class VueI18n {
pluralizationRules: {
[lang: string]: (choice: number, choicesLength: number) => number
}
getChoiceIndex: GetChoiceIndex

constructor (options: I18nOptions = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
Expand Down Expand Up @@ -110,6 +111,41 @@ export default class VueI18n {
this._warnHtmlInMessage = options.warnHtmlInMessage || 'off'
this._postTranslation = options.postTranslation || null

/**
* @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
* @param choicesLength {number} an overall amount of available choices
* @returns a final choice index
*/
this.getChoiceIndex = (choice: number, choicesLength: number): number => {
const thisPrototype = Object.getPrototypeOf(this)
if (thisPrototype && thisPrototype.getChoiceIndex) {
const prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex: any)
return (prototypeGetChoiceIndex: GetChoiceIndex).call(this, choice, choicesLength)
}

// Default (old) getChoiceIndex implementation - english-compatible
const defaultImpl = (_choice: number, _choicesLength: number) => {
_choice = Math.abs(_choice)

if (_choicesLength === 2) {
return _choice
? _choice > 1
? 1
: 0
: 1
}

return _choice ? Math.min(_choice, 2) : 0
}

if (this.locale in this.pluralizationRules) {
return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
} else {
return defaultImpl(choice, choicesLength)
}
}


this._exist = (message: Object, key: Path): boolean => {
if (!message || !key) { return false }
if (!isNull(this._path.getPathValue(message, key))) { return true }
Expand Down Expand Up @@ -231,7 +267,6 @@ export default class VueI18n {

onComponentInstanceCreated (newI18n: I18n) {
if (this._componentInstanceCreatedListener) {
// $FlowFixMe
this._componentInstanceCreatedListener(newI18n, this)
}
}
Expand Down Expand Up @@ -680,34 +715,6 @@ export default class VueI18n {
return choices[choice].trim()
}
/**
* @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
* @param choicesLength {number} an overall amount of available choices
* @returns a final choice index
*/
getChoiceIndex (choice: number, choicesLength: number): number {
// Default (old) getChoiceIndex implementation - english-compatible
const defaultImpl = (_choice: number, _choicesLength: number) => {
_choice = Math.abs(_choice)
if (_choicesLength === 2) {
return _choice
? _choice > 1
? 1
: 0
: 1
}
return _choice ? Math.min(_choice, 2) : 0
}
if (this.locale in this.pluralizationRules) {
return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
} else {
return defaultImpl(choice, choicesLength)
}
}
tc (key: Path, choice?: number, ...values: any): TranslateResult {
return this._tc(key, this.locale, this._getMessages(), null, choice, ...values)
}
Expand Down
34 changes: 17 additions & 17 deletions test/unit/issues.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ describe('issues', () => {
locale: 'ru',
messages: {
ru: {
car: '0 машин | 1 машина | {n} машины | {n} машин'
car: '0 машин | {n} машина | {n} машины | {n} машин'
}
},
pluralizationRules: {
Expand All @@ -578,12 +578,12 @@ describe('issues', () => {
})
vm = new Vue({ i18n })

assert(vm.$tc('car', 0), '0 машин')
assert(vm.$tc('car', 1), '1 машина')
assert(vm.$tc('car', 2), '2 машины')
assert(vm.$tc('car', 4), '4 машины')
assert(vm.$tc('car', 12), '12 машин')
assert(vm.$tc('car', 21), '21 машина')
assert.strictEqual(vm.$tc('car', 0), '0 машин')
assert.strictEqual(vm.$tc('car', 1), '1 машина')
assert.strictEqual(vm.$tc('car', 2), '2 машины')
assert.strictEqual(vm.$tc('car', 4), '4 машины')
assert.strictEqual(vm.$tc('car', 12), '12 машин')
assert.strictEqual(vm.$tc('car', 21), '21 машина')
})

it('ensures backward-compatibility with #451', () => {
Expand Down Expand Up @@ -617,21 +617,21 @@ describe('issues', () => {


i18n = new VueI18n({
locale: 'en',
locale: 'ru',
messages: {
ru: {
car: '0 машин | 1 машина | {n} машины | {n} машин'
car: '0 машин | {n} машина | {n} машины | {n} машин'
}
}
})
vm = new Vue({ i18n })

assert(vm.$tc('car', 0), '0 машин')
assert(vm.$tc('car', 1), '1 машина')
assert(vm.$tc('car', 2), '2 машины')
assert(vm.$tc('car', 4), '4 машины')
assert(vm.$tc('car', 12), '12 машин')
assert(vm.$tc('car', 21), '21 машина')
assert.strictEqual(vm.$tc('car', 0), '0 машин')
assert.strictEqual(vm.$tc('car', 1), '1 машина')
assert.strictEqual(vm.$tc('car', 2), '2 машины')
assert.strictEqual(vm.$tc('car', 4), '4 машины')
assert.strictEqual(vm.$tc('car', 12), '12 машин')
assert.strictEqual(vm.$tc('car', 21), '21 машина')

// Set the default implementation back
VueI18n.prototype.getChoiceIndex = defaultImpl
Expand Down Expand Up @@ -662,14 +662,14 @@ describe('issues', () => {
},
formatter: {
interpolate (message, values, path) {
assert(path, testPath)
assert.strictEqual(path, testPath)

return null // pass the case to the default formatter
}
}
})

assert(i18n.t(testPath), 'Hello!')
assert.strictEqual(i18n.t(testPath), 'Hello!')
})
})

Expand Down

0 comments on commit 092065e

Please sign in to comment.