Skip to content

Commit

Permalink
Merge pull request #1143 from kobotoolbox/formbuilder-handles-transla…
Browse files Browse the repository at this point in the history
…tions

Formbuilder handles translations
  • Loading branch information
jnm authored Feb 24, 2017
2 parents 435b98f + 55578ae commit dbf0a09
Show file tree
Hide file tree
Showing 30 changed files with 614 additions and 110 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ jsapp/js/refactored.es6
whoosh_index
tmp
staticfiles
.cache
.sass-cache
webpack-stats.json
sensitive_data.json

test/compiled/*
6 changes: 6 additions & 0 deletions jsapp/fonts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
*.ttf
*.woff
*.woff2
*.scss
*.css
*.md
*.ijmap
*.otf
codepoints
17 changes: 17 additions & 0 deletions jsapp/js/editorMixins/editableForm.es6
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ export default assign({
saveButtonText,
} = this.buttonStates();

let translations = this.state.translations || [];

return (
<bem.FormBuilderHeader>
<bem.FormBuilderHeader__row m={['first', allButtonsDisabled ? 'disabled' : null]}>
Expand Down Expand Up @@ -550,6 +552,21 @@ export default assign({
: null }

</bem.FormBuilderHeader__cell>
<bem.FormBuilderHeader__cell m="translations">
{
(translations.length < 2) ?
<p>
{translations[0]}
</p>
:
<p>
{translations[0]}
<small>
{translations[1]}
</small>
</p>
}
</bem.FormBuilderHeader__cell>
<bem.FormBuilderHeader__cell m={'spacer'} />
<bem.FormBuilderHeader__cell m={'library-toggle'} >
<bem.FormBuilderHeader__button m={['showLibrary']}
Expand Down
9 changes: 7 additions & 2 deletions jsapp/js/editorMixins/existingForm.es6
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ export default {
componentDidMount () {
let uid = this.props.params.assetid;
stores.allAssets.whenLoaded(uid, (asset) => {
let translations = (asset.content && asset.content.translations
&& asset.content.translations.slice(0)) || [];
this.launchAppForSurveyContent(asset.content, {
name: asset.name,
translations: translations,
settings__style: asset.settings__style,
asset_uid: asset.uid,
asset_type: asset.asset_type,
Expand All @@ -32,9 +35,11 @@ export default {
if (isLibrary(this.context.router)) {
routeName = 'library';
} else {
if (stores.history.previousRoute == 'form-landing') {
if (stores.history.currentRoute === 'form-edit') {
routeName = 'form-landing';
params = {assetid: this.props.params.assetid};
params = {
assetid: this.props.params.assetid,
};
}
}

Expand Down
7 changes: 6 additions & 1 deletion jsapp/js/mixins.es6
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,19 @@ var dmix = {
},
survey: {
innerRender: function () {
var docTitle = this.state.name || t('Untitled');
let docTitle = this.state.name || t('Untitled');
let formList = this.makeHref('forms');
return (
<DocumentTitle title={`${docTitle} | KoboToolbox`}>
<bem.FormView m='scrollable'>
<bem.FormView__wrapper m='form'>
<bem.FormView__row>
<bem.FormView__cell m='overview'>
<bem.FormView__label m='title'>
<bem.FormView__link
m='close'
href={formList}
/>
{t('Form Overview')}
</bem.FormView__label>
{this.renderDeployments()}
Expand Down
6 changes: 3 additions & 3 deletions jsapp/scss/components/_kobo.asset-row.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@
}

.asset-row__celllink--name {
display: inline-block;
max-width:96%;
display: block;
max-width: 96%;
text-overflow: ellipsis;
overflow: hidden;
font-size: 15px;
vertical-align: top;
padding:4px 0px;
padding: 4px 0px;
}

.asset-type-icon + .asset-row__celllink--name {
Expand Down
16 changes: 16 additions & 0 deletions jsapp/scss/components/_kobo.form-builder.scss
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@
width:calc(100% - 400px);
padding:0px 20px;
}
&--translations {
p {
margin: 0 0 0 10px;
line-height: 15px;
padding-top: 10px;
font-size: 11px;

small {
display: block;
color: #888;
&:before {
content: '+ ';
}
}
}
}

&--buttonsTopRight {
width:280px;
Expand Down
6 changes: 6 additions & 0 deletions jsapp/scss/components/_kobo.form-view.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@
}
}

.form-view__link--close {
@extend .k-icon, .k-icon-close;
float: right;
margin-right: -10px;
}

// FIXME: rename this
.form-view__cell {
.asset-view__label {
Expand Down
21 changes: 21 additions & 0 deletions jsapp/scss/stylesheets/partials/form_builder/_card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,27 @@
cursor:text;
// white-space: pre;
}
.card__header-subtitle {
margin: 10px 0 0 0;
color: #aaa;
font-size: 12px;

&:before {
content: '+ ';
opacity: 0.5;
}
}
.card__header-subtitle-empty-value {
border: 1px solid #ffdbdb;
padding: 2px 3px;
border-radius: 3px;
background-color: #fff8f8;
}
.card__option-translation {
&--empty {
opacity: 0.6;
}
}
&.card--shaded .card__header-title {
opacity:0.3;
}
Expand Down
31 changes: 25 additions & 6 deletions jsapp/xlform/src/model.inputParser.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module.exports = do ->
delete item[key]
_.map(translations, (_t, i)->
_translated_val = val[i]
if _t and _t isnt translations.preferred_translation
if _t
lang_str = "#{key}::#{_t}"
else
lang_str = key
Expand Down Expand Up @@ -95,11 +95,27 @@ module.exports = do ->

inputParser.parseArr = parseArr
inputParser.parse = (o)->
translations = o.translations or [null]
if translations and '#null_translation' of o
translations.preferred_translation = o['#null_translation']
if o['#null_translation'] isnt null and null in translations
translations[translations.indexOf(null)] = 'UNNAMED'
translations = o.translations
if o['#active_translation_name']
_existing_active_translation_name = o['#active_translation_name']
delete o['#active_translation_name']

if translations
if translations.indexOf(null) is -1 # there is no unnamed translation
if _existing_active_translation_name
throw new Error('active translation set, but cannot be found')
o._active_translation_name = translations[0]
translations[0] = null
else if translations.indexOf(null) > 0
throw new Error("""
unnamed translation must be the first (primary) translation
translations need to be reordered or unnamed translation needs
to be given a name
""")
else if _existing_active_translation_name # there is already an active null translation
o._active_translation_name = _existing_active_translation_name
else
translations = [null]

# sorts groups and repeats into groups and repeats (recreates the structure)
if o.survey
Expand All @@ -111,6 +127,9 @@ module.exports = do ->
# settings is sometimes packaged as an array length=1
if o.settings and _.isArray(o.settings) and o.settings.length is 1
o.settings = o.settings[0]

o.translations = translations

o

inputParser.loadChoiceLists = (passedChoices, choices)->
Expand Down
17 changes: 17 additions & 0 deletions jsapp/xlform/src/model.row.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,23 @@ module.exports = do ->

return newRow

getTranslatedColumnKey: (col, whichone="primary")->
if whichone is "_2"
_t = @getSurvey()._translation_2
else
_t = @getSurvey()._translation_1
_key = "#{col}"
if _t isnt null
_key += "::#{_t}"
_key

getLabel: (whichone="primary")->
_col = @getTranslatedColumnKey("label", whichone)
if _col of @attributes
@getValue _col
else
null

finalize: ->
existing_name = @getValue("name")
unless existing_name
Expand Down
45 changes: 12 additions & 33 deletions jsapp/xlform/src/model.survey.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,16 @@ module.exports = do ->
@choices = new $choices.ChoiceLists([], _parent: @)
$inputParser.loadChoiceLists(options.choices || [], @choices)

@translations = options.translations or [null]
if @translations
# if 'preferred_translation' is set, then any reference to a null translation
# is actually a reference to the preferred translation.
if @translations.preferred_translation
_pt_index = @translations.indexOf(@translations.preferred_translation)
if _pt_index is -1
throw new Error("Translation #{@translations.preferred_translation} "
"not found in list: #{@translations.join(', ')}")
if not @translations.secondary_translation
# the secondary_translation is the next available translation in the list
@translations.secondary_translation = @translations[if _pt_index is 0 then 1 else 0]
else if -1 is @translations.indexOf(null)
# if no null translation exists, set the first two translations as the
# preferred and secondary
@translations.preferred_translation = @translations[0]
@translations.secondary_translation = @translations[1]
else
# if a null translation exists (e.g. with a column "label" in a survey with
# other "label::lang" columns) then it is the "preferred_translation" by default
_null_index = @translations.indexOf(null)
@translations.preferred_translation = @translations[_null_index]
_next_translation_index = (_null_index + 1) % @translations.length
@translations.secondary_translation = @translations[_next_translation_index]
if options.translations
@translations = options.translations
else
@translations = [null]

if options['_active_translation_name']
@active_translation_name = options['_active_translation_name']

@_translation_1 = @translations[0]
@_translation_2 = @translations[1]

if options.survey
if !$inputParser.hasBeenParsed(options)
Expand Down Expand Up @@ -154,15 +140,8 @@ module.exports = do ->
addlSheets =
choices: new $choices.ChoiceLists()


# pass interface parameters back to the server
if @translations and @translations.preferred_translation isnt null
obj['#null_translation'] = @translations.preferred_translation

# in case we had to rename the null translation to "UNNAMED" in order
# to "focus" the form builder on a named translation
if @translations and 'UNNAMED' in @translations
obj['#replace_with_null_translation'] = 'UNNAMED'
if @active_translation_name
obj['#active_translation_name'] = @active_translation_name

obj.translations = [].concat(@translations)

Expand Down
26 changes: 15 additions & 11 deletions jsapp/xlform/src/view.choices.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ module.exports = do ->
if cardText.find('.card__buttons__multioptions.js-expand-multioptions').length is 0
cardText.prepend $.parseHTML($viewTemplates.row.expandChoiceList())
@$el.html (@ul = $("<ul>", class: @ulClasses))
_ts = @model.getSurvey().translations
if @row.get("type").get("rowType").specifyChoice
for option, i in @model.options.models
new OptionView(model: option, cl: @model).render().$el.appendTo @ul
new OptionView(model: option, cl: @model, translations: _ts).render().$el.appendTo @ul
if i == 0
while i < 2
@addEmptyOption("Option #{++i}")
Expand Down Expand Up @@ -54,7 +55,8 @@ module.exports = do ->
addEmptyOption: (label)->
emptyOpt = new $choices.Option(label: label)
@model.options.add(emptyOpt)
new OptionView(model: emptyOpt, cl: @model).render().$el.appendTo @ul
_translations = @model.getSurvey().translations
new OptionView(model: emptyOpt, cl: @model, translations: _translations).render().$el.appendTo @ul
lis = @ul.find('li')
if lis.length == 2
lis.find('.js-remove-option').removeClass('hidden')
Expand All @@ -73,7 +75,7 @@ module.exports = do ->

class OptionView extends $baseView
tagName: "li"
className: "multioptions__option xlf-option-view xlf-option-view--depr"
className: "multioptions__option xlf-option-view xlf-option-view--depr"
events:
"keyup input": "keyupinput"
"click .js-remove-option": "remove"
Expand Down Expand Up @@ -129,15 +131,17 @@ module.exports = do ->
@d.append(@t)
@d.append(@c)
@$el.html(@d)
try
_tt = @model.getSurvey().getSurvey().translations
catch err
_tt = false

if _tt and _tt.secondary_translation
_t_opt = @model.get("label::#{_tt.secondary_translation}")
$("<small>").html("""
<span>🌐&nbsp;-&nbsp;</span>
_translation_2 = @options.translations[1]
if _translation_2 isnt undefined
_t_opt = @model.get("label::#{_translation_2}")
if !_t_opt
_no_t = _t("No translation")
_klss = ["card__option-translation",
"card__option-translation--empty"].join(" ")
_t_opt = """<span class="#{_klss}">#{_no_t}</span>"""
$("<small>", {className: 'secondary-translation'}).html("""
<span>+&nbsp;</span>
<span class="translated-text">#{_t_opt}</span>
""").appendTo(@$el)
@
Expand Down
8 changes: 8 additions & 0 deletions jsapp/xlform/src/view.row.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ module.exports = do ->
_renderRow: ->
@$el.html $viewTemplates.$$render('row.xlfRowView', @surveyView)
@$label = @$('.card__header-title')
@$sub_label = @$('.card__header-subtitle')
@$card = @$('.card')
@$header = @$('.card__header')
context = {warnings: []}
Expand All @@ -75,6 +76,13 @@ module.exports = do ->
@listView = new $viewChoices.ListView(model: cl, rowView: @).render()

@cardSettingsWrap = @$('.card__settings').eq(0)
_second_translation = @surveyView.survey._translation_2
if _second_translation isnt undefined
_second_val = @model.getLabel('_2')
if !_second_val
_no_t = _t("No translation")
_second_val = """<span class="card__header-subtitle-empty-value">#{_no_t}</span>"""
@$sub_label.html(_second_val).show()
@defaultRowDetailParent = @cardSettingsWrap.find('.card__settings__fields--question-options').eq(0)
for [key, val] in @model.attributesArray() when key is 'label' or key is 'type'
view = new $viewRowDetail.DetailView(model: val, rowView: @)
Expand Down
Loading

0 comments on commit dbf0a09

Please sign in to comment.