diff options
| author | Luc Donnet | 2018-02-09 15:06:14 +0100 | 
|---|---|---|
| committer | GitHub | 2018-02-09 15:06:14 +0100 | 
| commit | 8f2c42a276d7d752448c3fad9d411b8f6e4716ff (patch) | |
| tree | e97abcf575f059463fda54f15dba045f08467402 | |
| parent | a68053c85fdb55e43fdf1ae7d9aba95e31a148bb (diff) | |
| parent | 7b62e86c4d5cc878fd9178d2296c3f5498bcd1cb (diff) | |
| download | chouette-core-8f2c42a276d7d752448c3fad9d411b8f6e4716ff.tar.bz2 | |
Merge pull request #290 from af83/4126-add-i18n-js
4126 Add i18n to JS
33 files changed, 303 insertions, 164 deletions
| diff --git a/.gitignore b/.gitignore index acdb5e230..dd4d057ef 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@  /tmp  *~  public/assets/ +public/javascripts/i18n.js +public/javascripts/translations.js  # for vim users  *.swp @@ -103,6 +103,7 @@ gem 'will_paginate-bootstrap'  gem 'gretel'  gem 'country_select'  gem 'flag-icons-rails' +gem 'i18n-js'  # Format Output  gem 'json' diff --git a/Gemfile.lock b/Gemfile.lock index 2988f6e8f..046167e69 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -271,6 +271,8 @@ GEM        multi_xml (>= 0.5.2)      i18n (0.9.4)        concurrent-ruby (~> 1.0) +    i18n-js (3.0.4) +      i18n (~> 0.6, >= 0.6.6)      i18n-tasks (0.9.15)        activesupport (>= 4.0.2)        ast (>= 2.1.0) @@ -644,6 +646,7 @@ DEPENDENCIES    gretel    has_array_of!    htmlbeautifier +  i18n-js    i18n-tasks    inherited_resources    jbuilder (~> 2.0) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 4c5aff22f..6a79f7e8e 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -22,3 +22,6 @@  //= require_directory .  // require('whatwg-fetch')  // require('babel-polyfill') +//= require "i18n" +//= require "i18n/extended" +//= require "i18n/translations" diff --git a/app/assets/javascripts/i18n/extended.coffee b/app/assets/javascripts/i18n/extended.coffee new file mode 100644 index 000000000..aeb67bd09 --- /dev/null +++ b/app/assets/javascripts/i18n/extended.coffee @@ -0,0 +1,24 @@ +#= require i18n + +decorateI18n = (_i18n)-> +  _i18n.tc = (key, opts={}) -> +    out = _i18n.t(key, opts) +    out += " " if _i18n.locale == "fr" +    out + ":" + +  _i18n.model_name = (model, opts={}) -> +    last_key = if opts.plural then "other" else "one" +    _i18n.t("activerecord.models.#{model}.#{last_key}") + +  _i18n.attribute_name = (model, attribute, opts={}) -> +    _i18n.t("activerecord.attributes.#{model}.#{attribute}") + +  _i18n.enumerize = (enumerize, key, opts={}) -> +    I18n.t("enumerize.#{enumerize}.#{key}") + +  _i18n + +module?.exports = decorateI18n + +if I18n? +  decorateI18n(I18n) diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass index 35e1122f3..1e02ad586 100644 --- a/app/assets/stylesheets/components/_tables.sass +++ b/app/assets/stylesheets/components/_tables.sass @@ -9,7 +9,6 @@        font-weight: 700        border-bottom: 2px solid $darkgrey        vertical-align: middle -        > a          position: relative          display: block @@ -326,6 +325,26 @@      padding: 6px 8px      border-bottom: 2px solid rgba($grey, 0.5)      border-top: 1px solid rgba($grey, 0.5) +    text-transform: capitalize + +    .info-button +      position: absolute +      width: 20px +      height: 20px +      top: 0 +      right: 0 +      margin: 6px 8px +      button +        border: none +        background: $blue +        border-radius: 20px +        width: 100% +        height: 100% +        font-size: 12px +        line-height: 14px +        color: white +        outline: none +    .td      position: relative      padding: 6px 8px diff --git a/app/javascript/vehicle_journeys/components/ConfirmModal.js b/app/javascript/vehicle_journeys/components/ConfirmModal.js index 3bfc852fb..75e8a3932 100644 --- a/app/javascript/vehicle_journeys/components/ConfirmModal.js +++ b/app/javascript/vehicle_journeys/components/ConfirmModal.js @@ -7,7 +7,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan        <div className='modal-dialog'>          <div className='modal-content'>            <div className='modal-body'> -            <p> Voulez-vous valider vos modifications avant de changer de page? </p> +            <p> {I18n.t('vehicle_journeys.vehicle_journeys_matrix.modal_confirm')} </p>            </div>            <div className='modal-footer'>              <button @@ -31,11 +31,11 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan        </div>      </div>    ) -}  +}  ConfirmModal.propTypes = {    vehicleJourneys: PropTypes.array.isRequired,    modal: PropTypes.object.isRequired,    onModalAccept: PropTypes.func.isRequired,    onModalCancel: PropTypes.func.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/components/Filters.js b/app/javascript/vehicle_journeys/components/Filters.js index 2bd912e3e..f8697c930 100644 --- a/app/javascript/vehicle_journeys/components/Filters.js +++ b/app/javascript/vehicle_journeys/components/Filters.js @@ -46,10 +46,10 @@ export default function Filters({filters, pagination, missions, onFilter, onRese            <div className='ffg-row'>              {/* Plage horaire */}              <div className='form-group togglable'> -              <label className='control-label'>Plage horaire au départ de la course</label> +              <label className='control-label'>{I18n.t("vehicle_journeys.form.departure_range.label")}</label>                <div className='filter_menu'>                  <div className='form-group time filter_menu-item'> -                  <label className='control-label time'>Début</label> +                  <label className='control-label time'>{I18n.t("vehicle_journeys.form.departure_range.start")}</label>                    <div className='form-inline'>                      <div className='input-group time'>                        <input @@ -73,7 +73,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese                    </div>                  </div>                  <div className='form-group time filter_menu-item'> -                  <label className='control-label time'>Fin</label> +                  <label className='control-label time'>{I18n.t("vehicle_journeys.form.departure_range.end")}</label>                    <div className='form-inline'>                      <div className='input-group time'>                        <input @@ -101,7 +101,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese              {/* Switch avec/sans horaires */}              <div className='form-group has_switch'> -              <label className='control-label pull-left'>Afficher les courses sans horaires</label> +              <label className='control-label pull-left'>{I18n.t("vehicle_journeys.form.show_journeys_without_schedule")}</label>                <div className='form-group pull-left' style={{padding: 0}}>                  <div className='checkbox'>                    <label> @@ -110,8 +110,8 @@ export default function Filters({filters, pagination, missions, onFilter, onRese                        onChange={onToggleWithoutSchedule}                        checked={filters.query.withoutSchedule}                        ></input> -                    <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'> -                      {filters.query.withoutSchedule ? 'Oui' : 'Non'} +                    <span className='switch-label' data-checkedvalue={I18n.t("no")} data-uncheckedvalue={I18n.t("yes")}> +                      {filters.query.withoutSchedule ? I18n.t("yes") : I18n.t("no")}                      </span>                    </label>                  </div> @@ -122,7 +122,7 @@ export default function Filters({filters, pagination, missions, onFilter, onRese            <div className="ffg-row">              {/* Switch avec/sans calendrier */}              <div className='form-group has_switch'> -              <label className='control-label pull-left'>Afficher les courses avec calendrier</label> +              <label className='control-label pull-left'>{I18n.t("vehicle_journeys.form.show_journeys_with_calendar")}</label>                <div className='form-group pull-left' style={{padding: 0}}>                  <div className='checkbox'>                    <label> @@ -131,8 +131,8 @@ export default function Filters({filters, pagination, missions, onFilter, onRese                        onChange={onToggleWithoutTimeTable}                        checked={filters.query.withoutTimeTable}                        ></input> -                    <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'> -                      {filters.query.withoutTimeTable ? 'Oui' : 'Non'} +                    <span className='switch-label' data-checkedvalue={I18n.t("no")} data-uncheckedvalue={I18n.t("yes")}> +                      {filters.query.withoutTimeTable ? I18n.t("yes") : I18n.t("no")}                      </span>                    </label>                  </div> diff --git a/app/javascript/vehicle_journeys/components/Navigate.js b/app/javascript/vehicle_journeys/components/Navigate.js index 0158b8392..24843babc 100644 --- a/app/javascript/vehicle_journeys/components/Navigate.js +++ b/app/javascript/vehicle_journeys/components/Navigate.js @@ -17,8 +17,7 @@ export default function Navigate({ dispatch, vehicleJourneys, pagination, status    if(status.fetchSuccess == true) {      return (        <div className="pagination"> -        Liste des horaires {minVJ} à {maxVJ} sur {pagination.totalCount} - +        {I18n.t("vehicle_journeys.vehicle_journeys_matrix.pagination", {minVJ, maxVJ, total:pagination.totalCount})}          <form className='page_links' onSubmit={e => {e.preventDefault()}}>            <button              onClick={e => { @@ -53,4 +52,4 @@ Navigate.propTypes = {    status: PropTypes.object.isRequired,    pagination: PropTypes.object.isRequired,    dispatch: PropTypes.func.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/components/ToggleArrivals.js b/app/javascript/vehicle_journeys/components/ToggleArrivals.js index 9e7089be5..9a2b0097f 100644 --- a/app/javascript/vehicle_journeys/components/ToggleArrivals.js +++ b/app/javascript/vehicle_journeys/components/ToggleArrivals.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'  export default function ToggleArrivals({filters, onToggleArrivals}) {    return (      <div className='has_switch form-group inline'> -      <label htmlFor='toggleArrivals' className='control-label'>Afficher et éditer les horaires d'arrivée</label> +      <label htmlFor='toggleArrivals' className='control-label'>{I18n.t('vehicle_journeys.form.show_arrival_time')}</label>        <div className='form-group'>          <div className='checkbox'>            <label> diff --git a/app/javascript/vehicle_journeys/components/Tools.js b/app/javascript/vehicle_journeys/components/Tools.js index ee02e5a68..22ea44283 100644 --- a/app/javascript/vehicle_journeys/components/Tools.js +++ b/app/javascript/vehicle_journeys/components/Tools.js @@ -44,8 +44,8 @@ export default class Tools extends Component {            <DeleteVehicleJourneys disabled={!this.hasPolicy("destroy") || !editMode}/>          </ul> -        <span className='info-msg'>{actions.getSelected(vehicleJourneys).length} course(s) sélectionnée(s)</span> -        <button className='btn btn-xs btn-link pull-right' onClick={onCancelSelection}>Annuler la sélection</button> +        <span className='info-msg'>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.selected_journeys', {count: actions.getSelected(vehicleJourneys).length})}</span> +        <button className='btn btn-xs btn-link pull-right' onClick={onCancelSelection}>{I18n.t('vehicle_journeys.vehicle_journeys_matrix.cancel_selection')}</button>        </div>      )    } diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index 99a458f50..4a9432231 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -23,7 +23,7 @@ export default class VehicleJourney extends Component {      let ttURL = refURL + '/time_tables/' + tt.id      return ( -      <a href={ttURL} title='Voir le calendrier'><span className='fa fa-calendar' style={{ color: (tt.color ? tt.color : '#4B4B4B')}}></span></a> +      <a href={ttURL} title={I18n.t('vehicle_journeys.vehicle_journeys_matrix.show_timetable')}><span className='fa fa-calendar' style={{ color: (tt.color ? tt.color : '#4B4B4B')}}></span></a>      )    } @@ -32,7 +32,7 @@ export default class VehicleJourney extends Component {      let ttURL = refURL + '/purchase_windows/' + tt.id      return ( -      <a href={ttURL} title='Voir le calendrier commercial'><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '')}}></span></a> +      <a href={ttURL} title={I18n.t('vehicle_journeys.vehicle_journeys_matrix.show_purchase_window')}><span className='fa fa-calendar' style={{color: (tt.color ? tt.color : '')}}></span></a>      )    } @@ -65,7 +65,7 @@ export default class VehicleJourney extends Component {            }            >            <div className='strong mb-xs'>{this.props.value.short_id || '-'}</div> -          <div>{this.props.value.published_journey_name && this.props.value.published_journey_name != "non renseigné" ? this.props.value.published_journey_name : '-'}</div> +          <div>{this.props.value.published_journey_name && this.props.value.published_journey_name != I18n.t('undefined') ? this.props.value.published_journey_name : '-'}</div>            <div>{this.props.value.journey_pattern.short_id || '-'}</div>            <div>{this.props.value.company ? this.props.value.company.name : '-'}</div>            <div> @@ -100,7 +100,7 @@ export default class VehicleJourney extends Component {            <div key={i} className='td text-center'>              <div className={'cellwrap' + (this.cityNameChecker(vj) ? ' headlined' : '')}>                {this.props.filters.toggleArrivals && -                <div data-headline='Arrivée à'> +                <div data-headline={I18n.t("vehicle_journeys.form.arrival_at")}>                    <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}>                      <input                        type='number' @@ -131,7 +131,7 @@ export default class VehicleJourney extends Component {                      <span className='sb sb-chrono sb-lg text-warning' data-textinside={vj.delta}></span>                    }                  </div> -                <div data-headline='Départ à'> +                <div data-headline={I18n.t("vehicle_journeys.form.departure_at")}>                    <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}>                      <input                        type='number' diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index ae852b35a..01e07ee0c 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -106,14 +106,14 @@ export default class VehicleJourneys extends Component {            <div className='col-lg-12'>              {(this.props.status.fetchSuccess == false) && (                <div className='alert alert-danger mt-sm'> -                <strong>Erreur : </strong> -                la récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème. +                <strong>{I18n.tc("error")}</strong> +                {I18n.t("vehicle_journeys.vehicle_journeys_matrix.fetching_error")}                </div>              )}              { this.vehicleJourneysList().errors && this.vehicleJourneysList().errors.length && _.some(this.vehicleJourneysList(), 'errors') && (                <div className="alert alert-danger mt-sm"> -                <strong>Erreur : </strong> +                <strong>{I18n.tc("error")}</strong>                  {this.vehicleJourneysList().map((vj, index) =>                    vj.errors && vj.errors.map((err, i) => {                      return ( @@ -129,12 +129,12 @@ export default class VehicleJourneys extends Component {              <div className={'table table-2entries mt-sm mb-sm' + ((this.vehicleJourneysList().length > 0) ? '' : ' no_result')}>                <div className='t2e-head w20'>                  <div className='th'> -                  <div className='strong mb-xs'>ID course</div> -                  <div>Nom course</div> -                  <div>ID mission</div> -                  <div>Transporteur</div> -                  <div>Calendriers</div> -                  { this.hasFeature('purchase_windows') && <div>Calendriers Commerciaux</div> } +                  <div className='strong mb-xs'>{I18n.attribute_name("vehicle_journey", "id")}</div> +                  <div>{I18n.attribute_name("vehicle_journey", "name")}</div> +                  <div>{I18n.attribute_name("vehicle_journey", "journey_pattern_id")}</div> +                  <div>{I18n.model_name("company")}</div> +                  <div>{I18n.model_name("time_table", "plural": true)}</div> +                  { this.hasFeature('purchase_windows') && <div>{I18n.model_name("purchase_window", "plural": true)}</div> }                  </div>                  {this.stopPoints().map((sp, i) =>{                    return ( diff --git a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js index f6a0e3c61..d3c01f154 100644 --- a/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/tools/EditVehicleJourney.js @@ -59,7 +59,7 @@ export default class EditVehicleJourney extends Component {                <div className='modal-dialog'>                  <div className='modal-content'>                    <div className='modal-header'> -                    <h4 className='modal-title'>Informations</h4> +                    <h4 className='modal-title'>{I18n.t('vehicle_journeys.form.infos')}</h4>                      <span type="button" className="close modal-close" data-dismiss="modal">×</span>                    </div> @@ -69,7 +69,7 @@ export default class EditVehicleJourney extends Component {                            <div className='row'>                              <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                                <div className='form-group'> -                                <label className='control-label'>Nom de la course</label> +                                <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'journey_name')}</label>                                  <input                                    type='text'                                    ref='published_journey_name' @@ -82,7 +82,7 @@ export default class EditVehicleJourney extends Component {                              </div>                              <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                                <div className='form-group'> -                                <label className='control-label'>Mission</label> +                                <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'journey_pattern')}</label>                                  <input                                    type='text'                                    className='form-control' @@ -96,7 +96,7 @@ export default class EditVehicleJourney extends Component {                          <div className='row'>                            <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                              <div className='form-group'> -                              <label className='control-label'>Numéro de train</label> +                              <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company')}</label>                                <input                                  type='text'                                  ref='published_journey_identifier' @@ -109,7 +109,7 @@ export default class EditVehicleJourney extends Component {                            </div>                            <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                              <div className='form-group'> -                              <label className='control-label'>Transporteur</label> +                              <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'company')}</label>                                <CompanySelect2                                  editModal={this.props.modal.type == "edit"}                                  editMode={this.editMode()} @@ -124,29 +124,29 @@ export default class EditVehicleJourney extends Component {                          <div className='row'>                            <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                              <div className='form-group'> -                              <label className='control-label'>Mode de transport</label> +                              <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'transport_mode')}</label>                                <input                                  type='text'                                  className='form-control' -                                value={window.I18n.fr.enumerize.transport_mode[this.props.modal.modalProps.vehicleJourney.transport_mode]} +                                value={I18n.enumerize('transport_mode', this.props.modal.modalProps.vehicleJourney.transport_mode)}                                  disabled={true}                                />                              </div>                            </div>                            <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'>                              <div className='form-group'> -                              <label className='control-label'>Sous mode de transport</label> +                              <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'transport_submode')}</label>                                <input                                  type='text'                                  className='form-control' -                                value={window.I18n.fr.enumerize.transport_submode[this.props.modal.modalProps.vehicleJourney.transport_submode]} +                                value={I18n.enumerize('transport_submode', this.props.modal.modalProps.vehicleJourney.transport_submode)}                                  disabled={true}                                />                              </div>                            </div>                          </div>                          <div className='form-group'> -                          <label className='control-label'>Signature métier</label> +                          <label className='control-label'>{I18n.attribute_name('vehicle_journey', 'checksum')}</label>                              <input                              type='text'                              ref='checksum' diff --git a/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js b/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js index a63a1d701..538bbdbd6 100644 --- a/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js +++ b/app/javascript/vehicle_journeys/components/tools/VehicleJourneyInfoButton.js @@ -10,7 +10,7 @@ export default class VehicleJourneyInfoButton extends Component {    render() {      return ( -      <li className='st_action'> +      <div className='info-button'>          <button            type='button'            data-toggle='modal' @@ -19,7 +19,7 @@ export default class VehicleJourneyInfoButton extends Component {          >            <span className='fa fa-info'></span>          </button> -      </li> +      </div>      )    }  } diff --git a/app/views/calendars/_form_advanced.html.slim b/app/views/calendars/_form_advanced.html.slim index b4154166b..e796e2e36 100644 --- a/app/views/calendars/_form_advanced.html.slim +++ b/app/views/calendars/_form_advanced.html.slim @@ -2,7 +2,7 @@  = javascript_tag do    | window.actionType = "#{raw params[:action]}"; -  | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; +  // | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe};    | window.timetablesUrl = "#{calendar_url(@calendar).html_safe}";  = javascript_pack_tag 'calendars/edit.js' diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 34b373295..3921c8701 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -13,6 +13,8 @@ html lang=I18n.locale      = javascript_pack_tag 'application'      = javascript_include_tag 'application' +    = javascript_tag do +      | I18n.locale = '#{I18n.locale}'    body      = render 'layouts/navigation/main_nav' diff --git a/app/views/routes/_form.html.slim b/app/views/routes/_form.html.slim index 29e5be3d2..81f719437 100644 --- a/app/views/routes/_form.html.slim +++ b/app/views/routes/_form.html.slim @@ -27,7 +27,7 @@  // Get JSON data for route stop points  = javascript_tag do    | window.itinerary_stop = "#{URI.escape(route_json_for_edit(@route))}"; -  | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; +  // | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe};  / StopPoints Reactux component  = javascript_pack_tag 'routes/edit.js' diff --git a/app/views/time_tables/edit.html.slim b/app/views/time_tables/edit.html.slim index e1c566ff4..d8cffb1b0 100644 --- a/app/views/time_tables/edit.html.slim +++ b/app/views/time_tables/edit.html.slim @@ -8,6 +8,6 @@  = javascript_tag do    | window.actionType = "#{raw params[:action]}"; -  | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe}; +  // | window.I18n = #{(I18n.backend.send(:translations)[I18n.locale].to_json).html_safe};  = javascript_pack_tag 'time_tables/edit.js' diff --git a/app/views/time_tables/index.html.slim b/app/views/time_tables/index.html.slim index f58fbb5ea..6913712a0 100644 --- a/app/views/time_tables/index.html.slim +++ b/app/views/time_tables/index.html.slim @@ -61,6 +61,6 @@            = replacement_msg t('time_tables.search_no_results')  = javascript_tag do -  | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; +  // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};  = javascript_pack_tag 'date_filters' diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index caa8450a0..d53d8b50c 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -29,7 +29,7 @@    | window.features = #{raw @features};    | window.all_missions = #{(@all_missions.to_json).html_safe};    | window.custom_fields = #{(@custom_fields.to_json).html_safe}; -  | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; +  // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};  - if has_feature?(:vehicle_journeys_return_route)    = javascript_tag do diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index a162ca334..aae34c51b 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -72,6 +72,6 @@            = replacement_msg t('referentials.search_no_results')  = javascript_tag do -  | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe}; +  // | window.I18n = #{(I18n.backend.send(:translations).to_json).html_safe};  = javascript_pack_tag 'date_filters' diff --git a/config/deploy.rb b/config/deploy.rb index 9be023adc..fc60113ce 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -78,7 +78,7 @@ namespace :deploy do      run "ln -nfs #{shared_path}/tmp/imports #{release_path}/tmp/imports"    end    after 'deploy:update_code', 'deploy:symlink_shared' -  before 'deploy:assets:precompile', 'deploy:symlink_shared' +  before 'deploy:assets:precompile', 'deploy:symlink_shared', "deploy:i18n_js_export"    desc "Make group writable all deployed files"    task :group_writable do diff --git a/config/environments/development.rb b/config/environments/development.rb index 1d2fee44f..446e72190 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -95,6 +95,7 @@ Rails.application.configure do    config.i18n.available_locales = [:fr, :en]    config.middleware.insert_after(ActionDispatch::Static, Rack::LiveReload) if ENV['LIVERELOAD'] +  config.middleware.use I18n::JS::Middleware    config.development_toolbar = false    if ENV['TOOLBAR'] && File.exists?("config/development_toolbar.rb")      config.development_toolbar = OpenStruct.new diff --git a/config/locales/en.yml b/config/locales/en.yml index e59960f95..8af8067db 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3,6 +3,7 @@ en:    "false": "No"    "unknown": "Unknown" +    time:      formats:        hour: "%Hh%M" @@ -61,3 +62,7 @@ en:    reflex_data: 'Reflex datas'    objectid: 'ID'    brandname: IBOO +  error: "Error" +  undefined: 'undefined' +  "yes": yes +  "no": no diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 175b71ebc..e1f52ff55 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -61,3 +61,7 @@ fr:    reflex_data: 'Données Reflex'    objectid: 'ID'    brandname: IBOO +  error: "Erreur" +  undefined: 'non renseigné' +  "yes": oui +  "no": non diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml index abb1da530..0c8a75b0c 100644 --- a/config/locales/vehicle_journeys.en.yml +++ b/config/locales/vehicle_journeys.en.yml @@ -1,7 +1,14 @@  en:    vehicle_journeys:      vehicle_journeys_matrix: +      cancel_selection: "Cancel Selection" +      fetching_error: "There has been a problem fetching the data. Please reload the page to try again."        line_routes: "Line's routes" +      modal_confirm: 'Do you want to save mofications before moving on to the next page ?' +      pagination: "Schedules %{minVJ} to %{maxVJ} over %{total}" +      selected_journeys: "%{count} selected journeys" +      show_purchase_window: 'Show the purchase window' +      show_timetable: 'Show calendar'      vehicle_journey:        title_stopless: "Vehicle journey %{name}"        title: "Vehicle journey leaving from %{stop} at %{time}" @@ -25,44 +32,54 @@ en:        title_stopless: "Update vehicle journey %{name}"        title: "Update vehicle journey %{name} leaving from %{stop} at %{time}"      form: -      stop_title: "Stop" -      departure: "Departure" +      arrival_at: "Arrival at"        arrival: "Arrival" -      to_arrivals: "Copy departures to arrivals" -      to_departures: "Copy arrivals to departures" -      time_tables: "Associated calendars to vehicle journey" -      slide: "Shift" -      slide_title: "Shift all vehicle passing times" +      departure_at: "Departure at" +      departure: "Departure" +      departure_range: +        label: Journey departure range +        start: Start +        end: End +      infos: Informations        set: "Set" -      to: "at" -      slide_departure: "departure time at first stop" +      show_arrival_time: "Show and edit arrival times" +      show_journeys_with_calendar: "Show journeys with calendar" +      show_journeys_without_schedule: "Show journeys without schedule"        slide_arrival: "arrival time at first stop" -      submit_timed: "Create vehicle journey" +      slide_departure: "departure time at first stop" +      slide_title: "Shift all vehicle passing times" +      slide: "Shift" +      stop_title: "Stop" +      submit_frequency_edit: "Edit frequency vehicle journey"        submit_frequency: "Create frequency vehicle journey"        submit_timed_edit: "Edit vehicle journey" -      submit_frequency_edit: "Edit frequency vehicle journey" +      submit_timed: "Create vehicle journey" +      time_tables: "Associated calendars to vehicle journey" +      to_arrivals: "Copy departures to arrivals" +      to_departures: "Copy arrivals to departures" +      to: "at"      timeless:        title: "Timeless vehicle journeys"        vehicle_journeys: "Vehicle journeys with times at stop"        vehicles_list: "Vehicle journeys list"      show: -      title: "Vehicle Journey %{vehicle journey}" -      stop_title: "Stop" -      departure: "Departure"        arrival: "Arrival" -      time_tables: "Calendars list"        bounding: "From %{start} to %{end}" -      translation_form: "Vehicle journey translations" +      departure: "Departure"        journey_frequencies: "Timeband" +      stop_title: "Stop" +      time_tables: "Calendars list" +      title: "Vehicle Journey %{vehicle journey}" +      translation_form: "Vehicle journey translations"      index: -      title: "Vehicle journeys on route %{route}" -      vehicle_journeys: "Departure's times" -      selection: "Filter on" -      selection_all: "All" +      advanced_search: "Advanced Search"        select_journey_patterns: "Select journey pattern"        select_time_tables: "Enter a timetable" +      selection_all: "All" +      selection: "Filter on"        time_range: "Departure time threshold" -      advanced_search: "Advanced Search" +      title: "Vehicle journeys on route %{route}" +      vehicle_journeys: "Departure's times"      time_filter:        time_range_filter: "Filter"      sidebar: @@ -78,40 +95,47 @@ en:          other: "vehicle journeys"      attributes:        vehicle_journey: -        line: "Line" -        route: "Route" -        journey_pattern: "Journey Pattern" -        time_tables: "Calendars" -        time_slot: "Time Slot" -        company: "Company" -        number: "Number" +        accessible: "Accessible" +        arrival_time: "Arrival" +        checksum: "Checksum"          comment: "Comments" -        status_value: "Status Value" -        transport_mode: "Transport Mode" -        mobility_restricted_suitability: "PRM accessibility" +        company: "Company" +        created_at: Created at +        creator_id: "Created by" +        departure_time: "Departure" +        facility: "Facility"          flexible_service: "On demond transportation" -        unspecified_mrs: "Not specified" -        accessible: "Accessible" +        footnote_ids: "Footnotes" +        id: "Journey ID" +        journey_frequency_ids: "Timeband" +        journey_name: "Name of the journey" +        journey_pattern_id: "Pattern ID" +        journey_pattern: "Journey Pattern" +        line: "Line" +        mobility_restricted_suitability: "PRM accessibility" +        name: "Journey Name"          not_accessible: "Not accessible" -        unspecified_fs: "Not specified" +        number: "Number" +        object_version: "Version" +        objectid: "Neptune identifier"          on_demand_fs: "On demand service" -        regular_fs: "Regular service" -        published_journey_name: "Published Name"          published_journey_identifier: "Published Identifier" -        facility: "Facility" -        vehicle_type_identifier: "Vehicle Type Identifier" +        published_journey_name: "Published Name" +        purchase_window: "Purchase availability" +        regular_fs: "Regular service" +        route: "Route" +        status_value: "Status Value" +        time_slot: "Time Slot"          time_table_ids: "Calendar list" -        vehicle_journey_at_stop_ids: "Time list" -        journey_frequency_ids: "Timeband" -        objectid: "Neptune identifier" -        object_version: "Version" -        created_at: Created at +        time_tables: "Calendars" +        train_number: "Train number" +        transport_mode: "Transport Mode" +        transport_submode: "Transport Submode" +        unspecified_fs: "Not specified" +        unspecified_mrs: "Not specified"          updated_at: Updated at -        creator_id: "Created by" -        footnote_ids: "Footnotes" -        departure_time: "Departure" -        arrival_time: "Arrival" -        purchase_window: "Purchase availability" +        vehicle_journey_at_stop_ids: "Time list" +        vehicle_type_identifier: "Vehicle Type Identifier"      errors:        models:          vehicle_journey: diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml index ca8475812..6bf167234 100644 --- a/config/locales/vehicle_journeys.fr.yml +++ b/config/locales/vehicle_journeys.fr.yml @@ -1,7 +1,14 @@  fr:    vehicle_journeys:      vehicle_journeys_matrix: +      cancel_selection: "Annuler la sélection" +      fetching_error: "La récupération des missions a rencontré un problème. Rechargez la page pour tenter de corriger le problème."        line_routes: "Séquences d'arrêts de la ligne" +      modal_confirm: 'Voulez-vous valider vos modifications avant de changer de page?' +      pagination: "Liste des horaires %{minVJ} à %{maxVJ} sur %{total}" +      selected_journeys: "%{count} course(s) sélectionnée(s)" +      show_purchase_window: 'Voir le calendrier commercial' +      show_timetable: 'Voir le calendrier'      vehicle_journey:        title_stopless: "Course %{name}"        title: "Course partant de %{stop} à %{time}" @@ -25,44 +32,55 @@ fr:        title_stopless: "Editer la course %{name}"        title: "Editer la course partant de %{stop} à %{time}"      form: -      stop_title: "Arrêt" -      departure: "Départ" +      arrival_at: "Arrivée à"        arrival: "Arrivée" -      to_arrivals: "Copie départs vers arrivées" -      to_departures: "Copie arrivées vers départs" -      time_tables: "Calendriers associés à la course" -      slide: "Décaler" -      slide_title: "Décaler l'ensemble des horaires de course" +      departure_at: "Départ à" +      departure: "Départ" +      departure_range: +        label: Plage horaire au départ de la course +        start: Début +        end: Fin +      infos: Informations        set: "Fixer" -      to: "à" -      slide_departure: "horaire de départ au 1° arrêt à" +      show_arrival_time: "Afficher et éditer les horaires d'arrivée" +      show_journeys_with_calendar: "Afficher les courses avec calendrier" +      show_journeys_without_schedule: "Afficher les courses sans horaires"        slide_arrival: "horaire d'arrivée au 1° arrêt à" -      submit_timed: "Créer course" +      slide_departure: "horaire de départ au 1° arrêt à" +      slide_title: "Décaler l'ensemble des horaires de course" +      slide: "Décaler" +      stop_title: "Arrêt" +      submit_frequency_edit: "Editer course en fréquence"        submit_frequency: "Créer course en fréquence"        submit_timed_edit: "Editer course" -      submit_frequency_edit: "Editer course en fréquence" +      submit_timed: "Créer course" +      time_tables: "Calendriers associés à la course" +      to_arrivals: "Copie départs vers arrivées" +      to_arrivals: "Copie départs vers arrivées" +      to_departures: "Copie arrivées vers départs" +      to: "à"      timeless:        title: "Courses sans horaire"        vehicle_journeys: "Courses ayant des horaires"        vehicles_list: "Liste des courses"      show: -      title: "Course au départ de %{stop} à %{time} sur la séquence %{route}" -      stop_title: "Arrêt" -      departure: "Départ"        arrival: "Arrivée" -      time_tables: "Liste des calendriers"        bounding: "De %{start} à %{end}" -      translation_form: "Cloner la course" +      departure: "Départ"        journey_frequencies: "Créneau horaire" +      stop_title: "Arrêt" +      time_tables: "Liste des calendriers" +      title: "Course au départ de %{stop} à %{time} sur la séquence %{route}" +      translation_form: "Cloner la course"      index: -      title: "Horaires de '%{route}'" -      vehicle_journeys: "Horaires de départ aux arrêts" -      selection: "Filtrer sur" -      selection_all: "Tous" +      advanced_search: "Recherche avancée"        select_journey_patterns: "Sélectionner une mission"        select_time_tables: "Saisir un calendrier" +      selection_all: "Tous" +      selection: "Filtrer sur"        time_range: "Seuil horaire au départ" -      advanced_search: "Recherche avancée" +      title: "Horaires de '%{route}'" +      vehicle_journeys: "Horaires de départ aux arrêts"      time_filter:        time_range_filter: "Filtrer"      sidebar: @@ -78,40 +96,47 @@ fr:          other: "courses"      attributes:        vehicle_journey: -        line: "Ligne" -        route: "Séquence d'arrêt" -        journey_pattern: "Mission" -        time_tables: "Calendriers" -        time_slot: "Fréquence" -        company: "Transporteur" -        number: "Numéro" +        accessible: "Accessible" +        arrival_time: "Arrivée" +        checksum: "Signature métier"          comment: "Commentaires" -        status_value: "Etat de trafic" -        transport_mode: "Mode de transport" -        mobility_restricted_suitability: "Accessibilité PMR" +        company: "Transporteur" +        created_at: "Créé le" +        creator_id: "Créé par" +        departure_time: "Départ" +        facility: "Equipement"          flexible_service: "Transport à la demande" -        unspecified_mrs: "Non spécifié" -        accessible: "Accessible" +        footnote_ids: "Notes de bas de page" +        id: "ID Course" +        journey_frequency_ids: "Créneau horaire" +        journey_name: "Nom de la course" +        journey_pattern_id: "ID Mission" +        journey_pattern: "Mission" +        line: "Ligne" +        mobility_restricted_suitability: "Accessibilité PMR" +        name: "Nom Course"          not_accessible: "Non accessible" -        unspecified_fs: "Non spécifié" +        number: "Numéro" +        object_version: "Version" +        objectid: "Identifiant Neptune"          on_demand_fs: "Service à la demande" -        regular_fs: "Service régulier" -        published_journey_name: "Nom public"          published_journey_identifier: "Identifiant public" -        facility: "Equipement" -        vehicle_type_identifier: "Type d'identifiant du véhicule" +        published_journey_name: "Nom public" +        purchase_window: "Disponibilité commerciale" +        regular_fs: "Service régulier" +        route: "Séquence d'arrêt" +        status_value: "Etat de trafic" +        time_slot: "Fréquence"          time_table_ids: "Liste des calendriers" -        vehicle_journey_at_stop_ids: "Liste des horaires" -        journey_frequency_ids: "Créneau horaire" -        objectid: "Identifiant Neptune" -        object_version: "Version" -        created_at: "Créé le" +        time_tables: "Calendriers" +        train_number: "Numéro de train" +        transport_mode: "Mode de transport" +        transport_submode: "Sous-mode de transport" +        unspecified_fs: "Non spécifié" +        unspecified_mrs: "Non spécifié"          updated_at: "Edité le" -        creator_id: "Créé par" -        footnote_ids: "Notes de bas de page" -        departure_time: "Départ" -        arrival_time: "Arrivée" -        purchase_window: "Disponibilité commerciale" +        vehicle_journey_at_stop_ids: "Liste des horaires" +        vehicle_type_identifier: "Type d'identifiant du véhicule"      errors:        models:          vehicle_journey: diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake index fdd813516..c46cf7416 100644 --- a/lib/tasks/ci.rake +++ b/lib/tasks/ci.rake @@ -31,15 +31,16 @@ namespace :ci do      sh "bundle exec bundle-audit check --update"    end -  task :spec => ["ci:assets","spec"] -    task :assets do      sh "RAILS_ENV=test bundle exec rake assets:precompile"    end -  task :jest => "ci:assets" do -    sh "yarn --no-progress install" # Hack to force install jest after webpack -    sh "node_modules/.bin/jest" +  task :i18n_js_export do +    sh "RAILS_ENV=test bundle exec rake i18n:js:export" +  end + +  task :jest do +    sh "node_modules/.bin/jest" unless ["CHOUETTE_JEST_DISABLED"]    end    desc "Deploy after CI" @@ -59,4 +60,4 @@ namespace :ci do  end  desc "Run continuous integration tasks (spec, ...)" -task :ci => ["ci:setup", "ci:spec", "ci:jest", "cucumber", "ci:check_security", "ci:deploy", "ci:clean"] +task :ci => ["ci:setup", "ci:assets", "ci:i18n_js_export", "spec", "ci:jest", "cucumber", "ci:check_security", "ci:deploy", "ci:clean"] diff --git a/package.json b/package.json index e80f5231e..25b158e3d 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,10 @@      "roots": [        "<rootDir>/spec/javascript"      ], +    "transform": { +      "^.+\\.coffee$": "<rootDir>/spec/javascript/preprocessor.js", +      "^.+\\.jsx?$": "babel-jest" +    },      "testEnvironment": "jest-environment-jsdom-global",      "setupFiles": [        "<rootDir>/spec/javascript/spec_helper.js", diff --git a/spec/javascript/preprocessor.js b/spec/javascript/preprocessor.js new file mode 100644 index 000000000..a2de8e4be --- /dev/null +++ b/spec/javascript/preprocessor.js @@ -0,0 +1,15 @@ +'use strict'; + +var coffee = require('coffeescript'); + +module.exports = { +  process: function(src, filename) { +    if (coffee.helpers.isCoffee(filename)) { +      return coffee.compile(src, { +        'bare': false, +        'inlineMap': true +      }) +    } +    return src; +  } +}; diff --git a/spec/javascript/vehicle_journeys/components/VehicleJourneys_spec.js b/spec/javascript/vehicle_journeys/components/VehicleJourneys_spec.js index 87151c64b..2a84cb9ca 100644 --- a/spec/javascript/vehicle_journeys/components/VehicleJourneys_spec.js +++ b/spec/javascript/vehicle_journeys/components/VehicleJourneys_spec.js @@ -1,6 +1,13 @@  import React, { Component } from 'react'  import VehicleJourneys from '../../../../app/javascript/vehicle_journeys/components/VehicleJourneys'  import renderer from 'react-test-renderer' +import fs from 'fs' + +import I18n from '../../../../public/javascripts/i18n' +import decorateI18n from '../../../../app/assets/javascripts/i18n/extended.coffee' +window.I18n = decorateI18n(I18n) +I18n.locale = "fr" +eval(fs.readFileSync('./public/javascripts/translations.js')+'')  describe('stopPointHeader', () => {    set('features', () => { diff --git a/spec/javascript/vehicle_journeys/components/__snapshots__/VehicleJourneys_spec.js.snap b/spec/javascript/vehicle_journeys/components/__snapshots__/VehicleJourneys_spec.js.snap index 703f727d7..cdd34cbbd 100644 --- a/spec/javascript/vehicle_journeys/components/__snapshots__/VehicleJourneys_spec.js.snap +++ b/spec/javascript/vehicle_journeys/components/__snapshots__/VehicleJourneys_spec.js.snap @@ -19,19 +19,19 @@ exports[`stopPointHeader should display the city name 1`] = `            <div              className="strong mb-xs"            > -            ID course +            ID Course            </div>            <div> -            Nom course +            Nom Course            </div>            <div> -            ID mission +            ID Mission            </div>            <div> -            Transporteur +            transporteur            </div>            <div> -            Calendriers +            calendrier            </div>          </div>          <div @@ -109,19 +109,19 @@ exports[`stopPointHeader with the "long_distance_routes" feature should display            <div              className="strong mb-xs"            > -            ID course +            ID Course            </div>            <div> -            Nom course +            Nom Course            </div>            <div> -            ID mission +            ID Mission            </div>            <div> -            Transporteur +            transporteur            </div>            <div> -            Calendriers +            calendrier            </div>          </div>          <div | 
