Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback to language-only file if language_territory file DNE #4

Merged
merged 4 commits into from
Aug 18, 2015
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ Create an instance of y18n with the config provided, options include:
* `directory`: the locale directory, default `./locales`.
* `updateFiles`: should newly observed strings be updated in file, default `true`.
* `locale`: what locale should be used.
* `fallbackToLanguage`: should fallback to a language-only file (e.g. `en.json`)
be allowed if a file matching the locale does not exist (e.g. `en_US.json`),
default `true`.

### y18n.\_\_(str, arg, arg, arg)

Expand Down
25 changes: 23 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ function Y18N (opts) {
this.directory = opts.directory || './locales'
this.updateFiles = typeof opts.updateFiles === 'boolean' ? opts.updateFiles : true
this.locale = opts.locale || 'en'
this.fallbackToLanguage = typeof opts.fallbackToLanguage === 'boolean' ? opts.fallbackToLanguage : true

// internal stuff.
this.cache = {}
Expand Down Expand Up @@ -53,7 +54,7 @@ Y18N.prototype._processWriteQueue = function () {
var locale = work[1]
var cb = work[2]

var languageFile = path.resolve(directory, './', locale + '.json')
var languageFile = this._resolveLocaleFile(directory, locale)
var serializedLocale = JSON.stringify(this.cache[locale], null, 2)

fs.writeFile(languageFile, serializedLocale, 'utf-8', function (err) {
Expand All @@ -65,7 +66,7 @@ Y18N.prototype._processWriteQueue = function () {

Y18N.prototype._readLocaleFile = function () {
var localeLookup = {}
var languageFile = path.resolve(this.directory, './', this.locale + '.json')
var languageFile = this._resolveLocaleFile(this.directory, this.locale)

try {
localeLookup = JSON.parse(fs.readFileSync(languageFile, 'utf-8'))
Expand All @@ -81,6 +82,26 @@ Y18N.prototype._readLocaleFile = function () {
this.cache[this.locale] = localeLookup
}

Y18N.prototype._resolveLocaleFile = function (directory, locale) {
var file = path.resolve(directory, './', locale + '.json')
if (this.fallbackToLanguage && !this._fileExistsSync(file) && ~locale.lastIndexOf('_')) {
// attempt fallback to language only
var languageFile = path.resolve(directory, './', locale.split('_')[0] + '.json')
if (this._fileExistsSync(languageFile)) file = languageFile
}
return file
}

// this only exists because fs.existsSync() "will be deprecated"
// see https://nodejs.org/api/fs.html#fs_fs_existssync_path
Y18N.prototype._fileExistsSync = function (file) {
try {
return fs.statSync(file).isFile()
} catch (err) {
return false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring an error stinks, but at least we're not using a deprecated function -- I think your right to avoid throwing in this library, we always want it to simply fallback to returning a default string.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Was trying to preserve this functionality from _readLocaleFile:

if (err.code === 'ENOENT') localeLookup = {}

}
}

Y18N.prototype.__n = function () {
var args = Array.prototype.slice.call(arguments)
var singular = args.shift()
Expand Down
74 changes: 72 additions & 2 deletions test/y18n-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ describe('y18n', function () {
__('Hello').should.equal('Avast ye mateys!')
})

it('uses language file if language_territory file does not exist', function () {
var __ = y18n({
locale: 'pirate_JM',
directory: __dirname + '/locales'
}).__

__('Hello').should.equal('Avast ye mateys!')
})

it('does not fallback to language file if fallbackToLanguage is false', function () {
var __ = y18n({
locale: 'pirate_JM',
fallbackToLanguage: false,
updateFiles: false,
directory: __dirname + '/locales'
}).__

__('Hello').should.equal('Hello')
})

it('uses strings as given if no matching locale files found', function () {
var __ = y18n({
locale: 'zz_ZZ',
updateFiles: false,
directory: __dirname + '/locales'
}).__

__('Hello').should.equal('Hello')
})

it('expands arguments into %s placeholders', function () {
var __ = y18n({
directory: __dirname + '/locales'
Expand All @@ -56,7 +86,7 @@ describe('y18n', function () {

describe('the first time observing a word', function () {
beforeEach(function (done) {
rimraf('./test/locales/fr.json', function () {
rimraf('./test/locales/fr*.json', function () {
return done()
})
})
Expand All @@ -72,17 +102,57 @@ describe('y18n', function () {

it('writes new word to locale file if updateFiles is true', function (done) {
var __ = y18n({
locale: 'fr',
locale: 'fr_FR',
directory: __dirname + '/locales'
}).__

__('banana', function (err) {
var locale = JSON.parse(fs.readFileSync('./test/locales/fr_FR.json', 'utf-8'))
locale.banana.should.equal('banana')
return done(err)
})
})

it('writes new word to language file if language_territory file does not exist', function (done) {
fs.writeFileSync('./test/locales/fr.json', '{"meow": "le meow"}', 'utf-8')

var __ = y18n({
locale: 'fr_FR',
directory: __dirname + '/locales'
}).__

__('meow').should.equal('le meow')
__('banana', function (err) {
var locale = JSON.parse(fs.readFileSync('./test/locales/fr.json', 'utf-8'))
locale.banana.should.equal('banana')
return done(err)
})
})

it('writes word to missing locale file, if no fallback takes place', function (done) {
fs.writeFileSync('./test/locales/fr.json', '{"meow": "le meow"}', 'utf-8')

var __ = y18n({
locale: 'fr_FR',
fallbackToLanguage: false,
directory: __dirname + '/locales'
}).__

__('banana', function (err) {
// 'banana' should be written to fr_FR.json
var locale = JSON.parse(fs.readFileSync('./test/locales/fr_FR.json', 'utf-8'))
locale.should.deep.equal({
banana: 'banana'
})
// fr.json should remain untouched
var frJson = JSON.parse(fs.readFileSync('./test/locales/fr.json', 'utf-8'))
frJson.should.deep.equal({
meow: 'le meow'
})
return done(err)
})
})

it('handles enqueuing multiple writes at the same time', function (done) {
var __ = y18n({
locale: 'fr',
Expand Down