ez-localize
Version:
Super-simple localization of strings in a Node/Browserify application
198 lines (151 loc) • 4.99 kB
text/coffeescript
_ = require 'lodash'
xlsx = require('xlsx')
# Extracts localized strings from a plain object
exports.extractLocalizedStrings = (obj) ->
if not obj?
return []
# Return self if string
if obj._base?
return [obj]
strs = []
# If array, concat each
if _.isArray(obj)
for item in obj
strs = strs.concat(@extractLocalizedStrings(item))
else if _.isObject(obj)
for key, value of obj
strs = strs.concat(@extractLocalizedStrings(value))
return strs
# Keep unique base language string combinations
exports.dedupLocalizedStrings = (strs) ->
out = []
keys = {}
for str in strs
key = str._base + ":" + str[str._base]
if keys[key]
continue
keys[key] = true
out.push(str)
return out
# Change the base locale for a set of localizations.
# Works by making whatever the user sees as the toLocale base
exports.changeBaseLocale = (strs, fromLocale, toLocale) ->
for str in strs
# Get displayed
if str[fromLocale]
displayed = str[fromLocale]
delete str[fromLocale]
else if str[str._base]
displayed = str[str._base]
delete str[str._base]
if displayed
str[toLocale] = displayed
str._base = toLocale
return
# Update a set of strings based on newly localized ones
exports.updateLocalizedStrings = (strs, updates) ->
# Regularize CR/LF and trim
regularize = (str) ->
return str.replace(/\r/g, "").trim()
# Map updates by key
updateMap = {}
for update in updates
updateMap[update._base + ":" + regularize(update[update._base])] = update
# Apply to each str
for str in strs
match = updateMap[str._base + ":" + regularize(str[str._base])]
if match?
for key, value of match
if key != "_base" and key != str._base and key != "_unused" # Also ignore unused
# Remove blank values
if value
str[key] = regularize(value)
else
delete str[key]
return
# Exports localized strings for specified locales to XLSX file.
exports.exportXlsx = (locales, strs) ->
wb = { SheetNames: [], Sheets: {} }
range = {s: {c:10000000, r:10000000}, e: {c:0, r:0 }}
ws = {}
addCell = (row, column, value) ->
# Update ranges
if range.s.r > row then range.s.r = row
if range.s.c > column then range.s.c = column
if range.e.r < row then range.e.r = row
if range.e.c < column then range.e.c = column
# Create cell
cell = { v: value, t: 's' }
cell_ref = xlsx.utils.encode_cell({c:column,r:row})
ws[cell_ref] = cell
localeCount = 0
addCell(0, localeCount++, "Original Language")
if not _.findWhere(locales, { code: "en"})
locales = locales.concat([{ code: "en", name: "English"}])
# Add locale columns
for locale in locales
addCell(0, localeCount++ ,locale.name)
# Add rows
rows = 0
for str in strs
if str._unused
continue
base = _.findWhere(locales, { code: str._base })
# Skip if unknown
if not base
continue
columns = 0
rows++
addCell(rows, columns++, base.name)
for locale in locales
addCell(rows, columns++, str[locale.code] or "")
# Encode range
if range.s.c < 10000000
ws['!ref'] = xlsx.utils.encode_range(range)
# Add worksheet to workbook */
wb.SheetNames.push("Translation")
wb.Sheets["Translation"] = ws
wbout = xlsx.write(wb, {bookType:'xlsx', bookSST:true, type: 'base64'})
return wbout
_ = require 'lodash'
xlsx = require 'xlsx'
# Import from base64 xlsx file, returning localized strings
exports.importXlsx = (locales, xlsxFile) ->
wb = xlsx.read(xlsxFile, {type: 'base64'})
ws = wb.Sheets[wb.SheetNames[0]]
# If English is not a locale, append it, as built-in form elements
# are specified in English
if not _.findWhere(locales, { code: "en"})
locales = locales.concat([{ code: "en", name: "English"}])
strs = []
# Get the range of cells
lastCell = ws["!ref"].split(":")[1]
totalColumns = xlsx.utils.decode_cell(lastCell).c + 1
totalRows = xlsx.utils.decode_cell(lastCell).r + 1
# For each rows
for i in [1...totalRows]
# Get base locale
base = _.findWhere(locales, { name: ws[xlsx.utils.encode_cell(c: 0, r: i)]?.v })
# Skip if unknown
if not base
continue
str = { _base: base.code }
for col in [1...totalColumns]
cell = ws[xlsx.utils.encode_cell(c: col, r: i)]
if not cell
continue
val = cell.v
# If value and not NaN, store in string
if val? and val != "" and val == val
# Convert to string
val = String(val)
else # Any invalid value is considered empty
val = ""
# Get locale of cell
locale = _.findWhere(locales, { name: ws[xlsx.utils.encode_cell(c: col, r: 0)]?.v })
if locale
str[locale.code] = val
# Ignore if base language blank
if str[str._base]
strs.push(str)
return strs