diff --git a/src/generators/app/index.js b/src/generators/app/index.js index b110938dd..6d03516ab 100644 --- a/src/generators/app/index.js +++ b/src/generators/app/index.js @@ -189,6 +189,11 @@ export class Generator extends Base { filter: val => val.toLowerCase() }, { type: 'confirm', + name: 'i18nSupport', + default: false, + message: 'Would you like to include i18n support (multi-language)?' + }, { + type: 'confirm', name: 'bootstrap', message: 'Would you like to include Bootstrap?' }, { @@ -219,6 +224,9 @@ export class Generator extends Base { this.filters.uibootstrap = !!answers.uibootstrap; insight.track('uibootstrap', !!answers.uibootstrap); + this.filters.i18nSupport = !!answers.i18nSupport; + insight.track('i18nSupport', !!answers.i18nSupport); + this.scriptExt = answers.transpiler === 'ts' ? 'ts' : 'js'; this.templateExt = answers.markup; @@ -444,6 +452,7 @@ export class Generator extends Base { if(this.filters.socketio) angModules.push("'btford.socket-io'"); if(this.filters.uirouter) angModules.push("'ui.router'"); if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'"); + if(this.filters.auth) { angModules.unshift(`'${this.scriptAppName}.admin'`); angModules.unshift(`'${this.scriptAppName}.auth'`); @@ -548,6 +557,10 @@ export class Generator extends Base { ['uiBootstrap', 'angular-ui-bootstrap'], ['ngMessages', 'angular-messages'], ['io', 'socket.io-client'], + ['i18n', 'angular-translate'], + ['i18nCookie', 'angular-translate-storage-cookie'], + ['i18nLocal', 'angular-translate-storage-local'], + ['i18nLoader', 'angular-translate-loader-static-files'], ['ngValidationMatch', 'angular-validation-match'] ]; function replacer(contents) { diff --git a/src/test/fixtures/.yo-rc.json b/src/test/fixtures/.yo-rc.json index ca971c119..1cfff6c65 100644 --- a/src/test/fixtures/.yo-rc.json +++ b/src/test/fixtures/.yo-rc.json @@ -21,6 +21,7 @@ "uirouter": true, "bootstrap": true, "uibootstrap": true, + "i18nSupport": false, "socketio": true, "auth": true, "models": true, diff --git a/src/test/get-expected-files.js b/src/test/get-expected-files.js index 73e5e00f7..15d0d6b54 100644 --- a/src/test/get-expected-files.js +++ b/src/test/get-expected-files.js @@ -136,6 +136,16 @@ export function app(options) { 'client/components/ui-router/ui-router.mock.' + script ]); } + + /* i18n Support */ + if (options.i18nSupport) { + files = files.concat([ + 'client/components/i18n/flags.png', + 'client/components/i18n/flags.' + stylesheet, + 'client/components/i18n/locale-en.json', + 'client/components/i18n/locale-fr.json' + ]); + } /* Ui-Bootstrap */ if (options.uibootstrap) { diff --git a/src/test/main.test.js b/src/test/main.test.js index d1010caf5..20c4cd056 100644 --- a/src/test/main.test.js +++ b/src/test/main.test.js @@ -27,7 +27,8 @@ const defaultOptions = { odms: ['mongoose'], auth: true, oauth: [], - socketio: true + socketio: true, + i18nSupport:false }; const TEST_DIR = __dirname; @@ -200,7 +201,8 @@ describe('angular-fullstack:app', function() { oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], socketio: true, bootstrap: true, - uibootstrap: true + uibootstrap: true, + i18nSupport:false }; before(function() { @@ -272,7 +274,8 @@ describe('angular-fullstack:app', function() { oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], socketio: true, bootstrap: true, - uibootstrap: true + uibootstrap: true, + i18nSupport:false }; this.retries(3); // Sequelize seems to be quite flaky @@ -343,6 +346,7 @@ describe('angular-fullstack:app', function() { odms: [], auth: false, oauth: [], + i18nSupport:false, socketio: false, bootstrap: false, uibootstrap: false diff --git a/templates/app/_package.json b/templates/app/_package.json index f103b8f5b..80722a812 100644 --- a/templates/app/_package.json +++ b/templates/app/_package.json @@ -19,6 +19,11 @@ "angular-socket-io": "~0.7.0",<% } if(filters.uirouter) { %> "angular-ui-router": "~0.3.1",<% } if(filters.auth) { %> "angular-validation-match": "^1.9.0",<% } %> + <% if (filters.i18nSupport) { %> + "angular-translate": "^2.13.1", + "angular-translate-loader-static-files": "^2.13.1", + "angular-translate-storage-cookie": "^2.13.1", + "angular-translate-storage-local": "^2.13.1", <% } %> <%# END CLIENT %> "core-js": "^2.2.1", "express": "^4.13.3", diff --git a/templates/app/client/app/account(auth)/login/login(html).html b/templates/app/client/app/account(auth)/login/login(html).html index 2f72c7268..c33d8ce6b 100644 --- a/templates/app/client/app/account(auth)/login/login(html).html +++ b/templates/app/client/app/account(auth)/login/login(html).html @@ -1,42 +1,42 @@
-

Login

-

Accounts are reset on server restart from server/config/seed.js. Default account is test@example.com / test

-

Admin account is admin@example.com / admin

+ translate="LOGIN"<% } %>><% if (!filters.i18nSupport) { %>Login<% } %> +

<% if (filters.i18nSupport) { %>{{'ACCOUNT_RESET_MSG' | translate}}<% } else { %>Accounts are reset on server restart from<% } %> server/config/seed.js. <% if (filters.i18nSupport) { %>{{'DEFAUL_ACCOUNT_MSG' | translate}}<% } else { %>Default account is<% } %> test@example.com / test

+

<% if (filters.i18nSupport) { %>{{'ADMIN_ACCOUNT_IS_MSG' | translate}}<% } else { %>Admin account is<% } %> admin@example.com / admin

- + translate="EMAIL"<% } %>><% if (!filters.i18nSupport) { %>Email<% } %>
- + translate="PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>Password<% } %>
-

- Please enter your email and password. +

translate="ENTER_EMAIL_PASSWORD_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Please enter your email and password.<% } %>

-

- Please enter a valid email. +

translate="ENTER_VALID_EMAIL_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Please enter a valid email.<% } %>

-

{{ vm.errors.login }}

+

{{ vm.errors.login <% if (filters.i18nSupport) { %> | translate<% } %>}}

- - ui-sref="signup"<% } else { %>href="/signup"<% } %>> - Register + ui-sref="signup"<% } else { %>href="/signup"<% } %><% if (filters.i18nSupport) { %> translate="REGISTER"<% } %>> + <% if (!filters.i18nSupport) { %>Register<% } %>
<% if (filters.oauth) { %> diff --git a/templates/app/client/app/account(auth)/login/login(pug).pug b/templates/app/client/app/account(auth)/login/login(pug).pug index a463419d4..4ee882d65 100644 --- a/templates/app/client/app/account(auth)/login/login(pug).pug +++ b/templates/app/client/app/account(auth)/login/login(pug).pug @@ -1,16 +1,16 @@ .container .row .col-sm-12 - h1 Login + h1<% if (filters.i18nSupport) { %>(translate="LOGIN")<% } else { %>Login<% } %> p - | Accounts are reset on server restart from + | <% if (filters.i18nSupport) { %>{{'ACCOUNT_RESET_MSG' | translate}}<% } else { %>Accounts are reset on server restart from<% } %> code server/config/seed.js - | . Default account is + | . <% if (filters.i18nSupport) { %>{{'DEFAUL_ACCOUNT_MSG' | translate}}<% } else { %>Default account is<% } %> code test@example.com | / code test p - | Admin account is + | <% if (filters.i18nSupport) { %>{{'ADMIN_ACCOUNT_IS_MSG' | translate}}<% } else { %>Admin account is<% } %> code admin@example.com | / code admin @@ -18,23 +18,23 @@ .col-sm-12 form.form(name='form', ng-submit='vm.login(form)', novalidate='') .form-group - label Email + label<% if (filters.i18nSupport) { %>(translate="EMAIL")<% } else { %>Email<% } %> input.form-control(type='email', name='email', ng-model='vm.user.email') .form-group - label Password + label<% if (filters.i18nSupport) { %>(translate="PASSWORD")<% } else { %>Password<% } %> input.form-control(type='password', name='password', ng-model='vm.user.password') .form-group.has-error - p.help-block(ng-show='form.email.$error.required && form.password.$error.required && vm.submitted') - | Please enter your email and password. - p.help-block {{ vm.errors.login }} + p.help-block(ng-show='form.email.$error.required && form.password.$error.required && vm.submitted'<% if (filters.i18nSupport) { %> translate="ENTER_EMAIL_PASSWORD_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>Please enter your email and password.<% } %> + p.help-block {{ vm.errors.login <% if (filters.i18nSupport) { %> | translate<% } %>}} div - button.btn.btn-inverse.btn-lg.btn-login(type='submit') - | Login + button.btn.btn-inverse.btn-lg.btn-login(type='submit'<% if (filters.i18nSupport) { %> translate="LOGIN"<% } %>) + | <% if (!filters.i18nSupport) { %>Login<% } %> = ' ' - a.btn.btn-default.btn-lg.btn-register(<% if (filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) - | Register + a.btn.btn-default.btn-lg.btn-register(<% if (filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %><% if (filters.i18nSupport) { %> translate="REGISTER"<% } %>) + | <% if (!filters.i18nSupport) { %>Register<% } %> <% if (filters.oauth) {%> hr diff --git a/templates/app/client/app/account(auth)/settings/settings(html).html b/templates/app/client/app/account(auth)/settings/settings(html).html index 690b0cf3f..2b98dffa8 100644 --- a/templates/app/client/app/account(auth)/settings/settings(html).html +++ b/templates/app/client/app/account(auth)/settings/settings(html).html @@ -1,13 +1,13 @@
-

Change Password

+ translate="CHANGE_PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>Change Password<% } %>
- + translate="CURRENT_PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>Current Password<% } %> @@ -17,34 +17,34 @@

Change Password

- + translate="NEW_PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>New Password<% } %>

- Password must be at least 3 characters. + ng-show="(form.newPassword.$error.minlength || form.newPassword.$error.required) && (form.newPassword.$dirty || vm.submitted)"<% if (filters.i18nSupport) { %> translate="PASSWORD_LENGTH_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Password must be at least 3 characters.<% } %>

- + translate="CONFIRM_NEW_PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>Confirm New Password<% } %>

- Passwords must match. + ng-show="form.confirmPassword.$error.match && vm.submitted"<% if (filters.i18nSupport) { %> translate="PASSWORDS_MATCH_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Passwords must match.<% } %>

{{ vm.message }}

- +
diff --git a/templates/app/client/app/account(auth)/settings/settings(pug).pug b/templates/app/client/app/account(auth)/settings/settings(pug).pug index fc4bee87f..d028cd2e5 100644 --- a/templates/app/client/app/account(auth)/settings/settings(pug).pug +++ b/templates/app/client/app/account(auth)/settings/settings(pug).pug @@ -1,11 +1,11 @@ .container .row .col-sm-12 - h1 Change Password + h1<% if (filters.i18nSupport) { %>(translate="CHANGE_PASSWORD")<% } else { %> Change Password<% } %> .col-sm-12 form.form(name='form', ng-submit='vm.changePassword(form)', novalidate='') .form-group - label Current Password + label<% if (filters.i18nSupport) { %>(translate="CURRENT_PASSWORD")<% } else { %> Current Password<% } %> input.form-control(type='password' name='password' ng-model='vm.user.oldPassword' @@ -13,23 +13,23 @@ p.help-block(ng-show='form.password.$error.mongoose') | {{ vm.errors.other }} .form-group - label New Password + label<% if (filters.i18nSupport) { %>(translate="NEW_PASSWORD")<% } else { %> New Password<% } %> input.form-control(type='password' name='newPassword' ng-model='vm.user.newPassword' ng-minlength='3', required='') - p.help-block(ng-show='(form.newPassword.$error.minlength || form.newPassword.$error.required) && (form.newPassword.$dirty || vm.submitted)') - | Password must be at least 3 characters. + p.help-block(ng-show='(form.newPassword.$error.minlength || form.newPassword.$error.required) && (form.newPassword.$dirty || vm.submitted)'<% if (filters.i18nSupport) { %> translate="PASSWORD_LENGTH_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>Password must be at least 3 characters.<% } %> .form-group - label Confirm New Password + label<% if (filters.i18nSupport) { %>(translate="CONFIRM_NEW_PASSWORD")<% } else { %> Confirm New Password<% } %> input.form-control(type='password' name='confirmPassword' ng-model='vm.user.confirmPassword' match="vm.user.newPassword" ng-minlength='3', required='') - p.help-block(ng-show='fvm.orm.confirmPassword.$error.match && vm.submitted') - | Passwords must match. + p.help-block(ng-show='fvm.orm.confirmPassword.$error.match && vm.submitted'<% if (filters.i18nSupport) { %> translate="PASSWORDS_MATCH_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>Passwords must match.<% } %> p.help-block {{ vm.message }} - button.btn.btn-lg.btn-primary(type='submit') Save changes + button.btn.btn-lg.btn-primary(type='submit'<% if (filters.i18nSupport) { %> translate="SAVE_CHANGES"<% } %>) <% if (!filters.i18nSupport) { %>Save changes<% } %> diff --git a/templates/app/client/app/account(auth)/signup/signup(html).html b/templates/app/client/app/account(auth)/signup/signup(html).html index 6b6cad4b7..11834260d 100644 --- a/templates/app/client/app/account(auth)/signup/signup(html).html +++ b/templates/app/client/app/account(auth)/signup/signup(html).html @@ -1,75 +1,75 @@
-

Sign up

+ translate="SIGN_UP"<% } %>><% if (!filters.i18nSupport) { %>Sign up<% } %>
- + translate="NAME"<% } %>><% if (!filters.i18nSupport) { %>Name<% } %> -

- A name is required +

translate="NAME_REQUIRED_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>A name is required<% } %>

- + translate="EMAIL"<% } %>><% if (!filters.i18nSupport) { %>Email<% } %> -

- Doesn't look like a valid email. +

translate="EMAIL_INVALID_FORMAT_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Doesn't look like a valid email.<% } %>

-

- What's your email address? +

translate="EMAIL_QUESTION_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>What's your email address?<% } %>

- {{ vm.errors.email }} + {{ vm.errors.email <% if (filters.i18nSupport) { %>| translate <% } %>}}

- + translate="PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>Password<% } %>

- Password must be at least 3 characters. + ng-show="(form.password.$error.minlength || form.password.$error.required) && vm.submitted"<% if (filters.i18nSupport) { %> translate="PASSWORD_LENGTH_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Password must be at least 3 characters.<% } %>

- {{ vm.errors.password }} + {{ vm.errors.password <% if (filters.i18nSupport) { %>| translate <% } %>}}

- + translate="CONFIRM_PASSWORD"<% } %>><% if (!filters.i18nSupport) { %>Confirm Password<% } %>

- Passwords must match. + ng-show="form.confirmPassword.$error.match && vm.submitted"<% if (filters.i18nSupport) { %> translate="PASSWORDS_MATCH_MSG"<% } %>> + <% if (!filters.i18nSupport) { %>Passwords must match.<% } %>

<% if (filters.oauth) { %> diff --git a/templates/app/client/app/account(auth)/signup/signup(pug).pug b/templates/app/client/app/account(auth)/signup/signup(pug).pug index 51480eefb..d82c894d4 100644 --- a/templates/app/client/app/account(auth)/signup/signup(pug).pug +++ b/templates/app/client/app/account(auth)/signup/signup(pug).pug @@ -1,58 +1,58 @@ .container .row .col-sm-12 - h1 Sign up + h1<% if (filters.i18nSupport) { %>(translate="SIGN_UP")<% } else { %> Sign up<% } %> .col-sm-12 form.form(name='form', ng-submit='vm.register(form)', novalidate='') .form-group(ng-class='{ "has-success": form.name.$valid && vm.submitted,\ "has-error": form.name.$invalid && vm.submitted }') - label Name + label<% if (filters.i18nSupport) { %>(translate="NAME")<% } else { %> Name<% } %> input.form-control(type='text', name='name', ng-model='vm.user.name', required='') - p.help-block(ng-show='form.name.$error.required && vm.submitted') - | A name is required + p.help-block(ng-show='form.name.$error.required && vm.submitted'<% if (filters.i18nSupport) { %> translate="NAME_REQUIRED_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>A name is required<% } %> .form-group(ng-class='{ "has-success": form.email.$valid && vm.submitted,\ "has-error": form.email.$invalid && vm.submitted }') - label Email + label<% if (filters.i18nSupport) { %>(translate="EMAIL")<% } else { %> Email<% } %> input.form-control(type='email', name='email', ng-model='vm.user.email', required='', mongoose-error='') - p.help-block(ng-show='form.email.$error.email && vm.submitted') - | Doesn't look like a valid email. - p.help-block(ng-show='form.email.$error.required && vm.submitted') - | What's your email address? + p.help-block(ng-show='form.email.$error.email && vm.submitted'<% if (filters.i18nSupport) { %> translate="EMAIL_INVALID_FORMAT_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>Doesn't look like a valid email.<% } %> + p.help-block(ng-show='form.email.$error.required && vm.submitted'<% if (filters.i18nSupport) { %> translate="EMAIL_QUESTION_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>What's your email address?<% } %> p.help-block(ng-show='form.email.$error.mongoose') - | {{ vm.errors.email }} + | {{ vm.errors.email <% if (filters.i18nSupport) { %>| translate<% } %>}} .form-group(ng-class='{ "has-success": form.password.$valid && vm.submitted,\ "has-error": form.password.$invalid && vm.submitted }') - label Password + label<% if (filters.i18nSupport) { %>(translate="PASSWORD")<% } else { %> Password<% } %> input.form-control(type='password' name='password' ng-model='vm.user.password' mongoose-error='' ng-minlength='3', required='') - p.help-block(ng-show='(form.password.$error.minlength || form.password.$error.required) && vm.submitted') - | Password must be at least 3 characters. + p.help-block(ng-show='(form.password.$error.minlength || form.password.$error.required) && vm.submitted'<% if (filters.i18nSupport) { %> translate="PASSWORD_LENGTH_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>Password must be at least 3 characters.<% } %> p.help-block(ng-show='form.password.$error.mongoose') - | {{ vm.errors.password }} + | {{ vm.errors.password <% if (filters.i18nSupport) { %>| translate<% } %>}} .form-group(ng-class='{ "has-success": form.confirmPassword.$valid && vm.submitted,\ "has-error": form.confirmPassword.$invalid && vm.submitted }') - label Confirm Password + label<% if (filters.i18nSupport) { %>(translate="CONFIRM_PASSWORD")<% } else { %> Confirm Password<% } %> input.form-control(type='password' name='confirmPassword' ng-model='vm.user.confirmPassword' match="vm.user.password" ng-minlength='3', required='') - p.help-block(ng-show='form.confirmPassword.$error.match && vm.submitted') - | Passwords must match. + p.help-block(ng-show='form.confirmPassword.$error.match && vm.submitted'<% if (filters.i18nSupport) { %> translate="PASSWORDS_MATCH_MSG"<% } %>) + | <% if (!filters.i18nSupport) { %>Passwords must match.<% } %> div - button.btn.btn-inverse.btn-lg.btn-register(type='submit') - | Sign up + button.btn.btn-inverse.btn-lg.btn-register(type='submit'<% if (filters.i18nSupport) { %> translate="SIGN_UP"<% } %>) + | <% if (!filters.i18nSupport) { %>Sign up<% } %> = ' ' - a.btn.btn-default.btn-lg.btn-login(<% if (filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) - | Login + a.btn.btn-default.btn-lg.btn-login(<% if (filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %><% if (filters.i18nSupport) { %> translate="LOGIN"<% } %>) + | <% if (!filters.i18nSupport) { %>Login<% } %> <% if (filters.oauth) {%> hr diff --git a/templates/app/client/app/admin(auth)/admin(html).html b/templates/app/client/app/admin(auth)/admin(html).html index cbbe68a40..829829d09 100644 --- a/templates/app/client/app/admin(auth)/admin(html).html +++ b/templates/app/client/app/admin(auth)/admin(html).html @@ -1,5 +1,5 @@
-

The delete user and user index api routes are restricted to users with the 'admin' role.

+

translate="ADMIN_RESTRICT_MSG"<% } %>><% if (!filters.i18nSupport) { %>The delete user and user index api routes are restricted to users with the 'admin' role.<% } %>