there
Version:
meanwhile call it stuff
173 lines (139 loc) • 7.35 kB
text/coffeescript
_ = require "underscore"
# Use this as a mixin for classes that have words to name / translate.
# See [lin](http://astrolet.github.com/lin/)'s `Ensemble`
# as an example of what the `@words` object must be like.
# For a more varied example of words / localization data, see
# [there](http://astrolet.github.com/there/)'s
# [care](http://astrolet.github.com/there/annotations/care.html).
# Suppose an attribute of "name" - quite usual, often the only (necessary).
# Thus expect that `@words.expect is ['name']`, while the actual words data
# must be in conformance with each `@words.data[id][attribute][language]`
# set to a `["Hermes Name", "Modern Name"]` array.
# **`Hermes Name`** is the Hellenistic Astrology (System of Hermes) name.
# If the **`Hermes Name`** is `true`, rather than a string, it means that
# the **`Modern Name`** is equivalent to the original Greek word.
# If it's `null` (with other languages), then it hasn't been revised,
# meaning it's possibly sub-optimal for Hellenistic Astrology use.
# A value of false means it's deemed unfit for Hellenistic Astrology.
# In order to be `@translatable` a collection needs to have its initial data
# set up. Look at [lin](http://astrolet.github.com/lin)'s
# [Ensemble](http://astrolet.github.com/lin/annotations/ensemble.html)
# - more pecifically how `@planets.the` and `@planets.attributes.use`
# are expected in addition to `@words`.
# There can be more than one initialization objects, but a single `@words`
# data set for translation - basically the collection as a whole.
# Example usage within the `constructor` / `initialize`:
# _.extend @, polyglot.ensure
# @translatable() is true
module.exports = {
ensure:
translatable: (inits) ->
# English words must be present (at least), but also many other things...
# It's kind of pointless to check for it all. Perhaps a try / catch - as
# another way to return `false`. Another idea: check if anything is to be
# `push`ed and have that determine the return value.
if @words? and not _.isEmpty inits
# Expected instance variables will get defaults if not already set.
@school ?= "h"
@language ?= "en"
# Will this work on the client-side?
_.extend @, module.exports.methods
# Initialize the translatable data.
for init in inits
# The same translatable attributes must be used across the collection.
# This isn't immediately obvious, looking at a single init hash.
for i in [0 .. @words.expect.length - 1]
expected = @words.expect[i] # the translated key
filledIn = @words.filler[i] # default, if no translation is present
sequence = init.the[0].length + i
init.attributes.use[expected] = sequence
# If each row's expected item is not in `words.data[{key}][{expect}]`
# add a default using `words.filler` data
# from a corresponding array position.
for row in init.the
identifier = row[init.attributes.use[init.attributes.key]]
substitute = row[init.attributes.use[filledIn]] # filled-in value
@words.data[identifier] ?= {}
unless @words.data[identifier][expected]?
# console.log "not found: key '#{identifier}' for '#{expected}'"
# console.log "using: #{substitute}"
addition = en: [ false, substitute ]
@words.data[identifier][expected] = addition
# It's important to call `@translation` with sorted keys,
# or else the `@words.data` order must match that of `init.the`.
i = 0
for attributes in _.values @translation @language # and further...
, @school
, _.pluck(init.the, init.attributes.use[init.attributes.key])
# Add each attribute (without "key_id", which usually is the "id").
for attribute, word of attributes
continue if attribute is "key_id"
init.the[i].push word
i++
true
else false
# The following are mixed in through an `if @translatable()` call / (check).
methods:
translation: ( language
, school
, keys = []
, select = @words.expect
) ->
switch @school
when "h" then position = 0 # hermes (hellenistic hindsight)
when "m" then position = 1 # modern
else throw "Unknown '#{@school}' position."
if _.isEmpty keys then keys = _.keys @words.data
else
# Sorted according to `keys` order (intersection does), so `@words.data`
# can be in any order.
keys = _.intersection keys, _.keys @words.data
# The `i` is immediately incremented for `out[i]` use.
out = []; i = -1
for key in keys
out[++i] = { "key_id": key }
for word in select
# The English language is used / mixed in for untranslated
# or partially translated stuff.
stuff = @words.data[key][word][@language] ? @words.data[key][word]['en']
if position is 0
value = stuff[position]
if _.isString value then out[i][word] = value
else
switch value
# The modern version is `true`, meaning it never deviated from
# its Hellenistic roots. Or, in the case of `null` -
# it's unknown if it will have to be translated.
# Modern is assumed to be ok for the time being.
# TODO: null doesn't seem to be picking the data (undefined)!
when true || null then out[i][word] = stuff[1]
# The modern version is unacceptable (what `false` means).
# English default is always available (at least as modern).
when false
out[i][word] = @words.data[key][word]['en'][position]
# The English fallback position 0, could be made more elegant.
# Probably this whole translation should be refactored,
# extracting a tranlateWord function to get in a language or
# else call it again for English.
unless _.isString out[i][word]
out[i][word] = @words.data[key][word]['en'][1]
# Assuming that every translated language has the modern vocabulary.
else out[i][word] = stuff[position]
out
# Use an optional array of `select` attributes for partial translation.
translate: (language = @language, school = @school, select) ->
# The current implementation doesn't force-reload
# the same language / school (pointless as the data is part of the code).
if @language isnt language or @school isnt school
@school = school
@language = language
for attributes in @translation @language
, @school
, @pluck('id')
, select
key = attributes.key_id
delete attributes.key_id
# Note that "get" is the default method for the `@words.data` key.
@[@words.model || 'get'](key).set attributes
@ # chainable
}