Skip to content

Commit

Permalink
feature: localization (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianthedev committed Nov 17, 2020
1 parent 3cd8de0 commit ec224a2
Show file tree
Hide file tree
Showing 114 changed files with 622 additions and 270 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.json
Expand Up @@ -28,9 +28,11 @@
// "sort-imports": ["error"],
"semi": ["error", "never"],
"no-multi-spaces": 0,
"no-shadow": 0,
"no-param-reassign": 0,
"camelcase": ["error", {"ignoreDestructuring": true}],
"newline-before-return": 1
"newline-before-return": 1,
"max-len": ["error", { "code": 180 }]
},
"settings": {
"import/resolver": {
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Expand Up @@ -67,6 +67,10 @@ jobs:
- name: Bundle assets
run: bin/webpack

- name: Export translations
run: bin/rails i18n:js:export
working-directory: spec/dummy

- name: Run tests
run: npm run test-all

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -10,8 +10,10 @@ spec/dummy/node_modules/
spec/dummy/assets/
spec/dummy/public/packs/
spec/dummy/public/packs-test/
spec/dummy/public/javascripts/
spec/dummy/tmp/
vendor/cache/
public/javascripts/

# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
Expand Down
7 changes: 5 additions & 2 deletions Gemfile
Expand Up @@ -59,8 +59,7 @@ gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'

gem 'factory_bot_rails'
gem 'faker'

gem 'faker', require: false
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Expand Down Expand Up @@ -94,3 +93,7 @@ gem 'zeitwerk', '~> 2.3'
gem 'kaminari'

gem 'httparty'

gem 'iso'

gem 'i18n-js'
7 changes: 7 additions & 0 deletions Gemfile.lock
Expand Up @@ -4,6 +4,7 @@ PATH
avo (0.3.2)
countries
httparty
i18n-js
inline_svg
kaminari
pundit
Expand Down Expand Up @@ -132,10 +133,14 @@ GEM
multi_xml (>= 0.5.2)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
i18n-js (3.8.0)
i18n (>= 0.6.6)
i18n_data (0.10.0)
inline_svg (1.7.1)
activesupport (>= 3.0)
nokogiri (>= 1.6)
iso (0.3.0)
i18n
jbuilder (2.10.1)
activesupport (>= 5.0.0)
kaminari (1.2.1)
Expand Down Expand Up @@ -341,7 +346,9 @@ DEPENDENCIES
fuubar
gem-release
httparty
i18n-js
inline_svg
iso
jbuilder (~> 2.7)
kaminari
listen (>= 3.0.5, < 3.2)
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/avo/application_controller.rb
Expand Up @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base
before_action :init_app

def init_app
Avo::App.boot if Avo::IN_DEVELOPMENT
Avo::App.init request

@license = Avo::App.license
Expand Down Expand Up @@ -57,7 +58,7 @@ def authorize_user
end

def render_unauthorized
render json: { message: 'Unauthorized' }, status: 403
render json: { message: I18n.t('avo.unauthorized') }, status: 403
end
end
end
4 changes: 2 additions & 2 deletions app/controllers/avo/relations_controller.rb
Expand Up @@ -9,7 +9,7 @@ def attach

render json: {
success: true,
message: "#{attachment_class} attached.",
message: I18n.t('avo.attachment_class_attached', attachment_class: attachment_class),
}
end

Expand All @@ -18,7 +18,7 @@ def detach

render json: {
success: true,
message: "#{attachment_class} attached.",
message: I18n.t('avo.attachment_class_detached', attachment_class: attachment_class),
}
end

Expand Down
1 change: 1 addition & 0 deletions app/controllers/avo/resource_overview_controller.rb
Expand Up @@ -5,6 +5,7 @@ class ResourceOverviewController < ApplicationController
def index
resources = App.get_resources
.select { |resource| AuthorizationService::authorize session_user, resource.model, Avo.configuration.authorization_methods.stringify_keys['index'] }
.sort_by(&:name)
.map do |resource|
{
name: resource.name,
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/avo/resources_controller.rb
Expand Up @@ -95,7 +95,7 @@ def update
render json: {
success: true,
resource: Avo::Resources::Resource.hydrate_resource(model: resource, resource: avo_resource, view: :show, user: current_user),
message: 'Resource updated',
message: I18n.t('avo.resource_updated'),
}
end

Expand All @@ -118,7 +118,7 @@ def create
render json: {
success: true,
resource: Avo::Resources::Resource.hydrate_resource(model: resource, resource: avo_resource, view: :create, user: current_user),
message: 'Resource created',
message: I18n.t('avo.resource_created'),
}
end

Expand All @@ -132,7 +132,7 @@ def destroy
resource.destroy!

render json: {
message: 'Resource destroyed',
message: I18n.t('avo.resource_destroyed'),
}
end

Expand Down Expand Up @@ -255,6 +255,7 @@ def build_meta
per_page_steps: Avo.configuration.per_page_steps,
available_view_types: avo_resource.available_view_types,
default_view_type: avo_resource.default_view_type || Avo.configuration.default_view_type,
translation_key: avo_resource.translation_key,
authorization: {
create: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['create']),
edit: AuthorizationService::authorize(current_user, avo_resource.model, Avo.configuration.authorization_methods.stringify_keys['edit']),
Expand Down
18 changes: 17 additions & 1 deletion app/frontend/js/Avo.js
@@ -1,14 +1,17 @@
import '@/js/components'
import Api from '@/js/Api'
import Bus from '@/js/Bus'
import I18n from 'i18n-js'
import PortalVue from 'portal-vue'
import Toasted from 'vue-toasted'
import VModal from 'vue-js-modal'
import VTooltip from 'v-tooltip'
import Vue from 'vue/dist/vue.esm'
import VueCurrencyInput from 'vue-currency-input'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import Vuex, { mapMutations } from 'vuex'

import appStore from '@/js/stores/app-store'
import indexStore from '@/js/stores/index-store'
import router from '@/js/router'

Expand Down Expand Up @@ -41,6 +44,7 @@ const Avo = {
return new Vuex.Store({
modules: {
index: indexStore,
app: appStore,
},
})
},
Expand Down Expand Up @@ -69,6 +73,13 @@ const Avo = {
})
Vue.use(PortalVue)
Vue.use(Vuex)

// Custom i18n plugin
Vue.use({
install(Vue) {
Vue.prototype.$t = (key, options) => I18n.t(key, options)
},
})
},

initVue() {
Expand All @@ -89,6 +100,9 @@ const Avo = {
},
},
methods: {
...mapMutations('app', [
'setAvailableResources',
]),
reload() {
this.$router.go()
},
Expand All @@ -104,6 +118,8 @@ const Avo = {
},
},
mounted() {
this.setAvailableResources(window.avoResources)

Bus.$on('reload', this.reload)
Bus.$on('redirect', this.redirect)
Bus.$on('message', (message) => this.alert(message, 'success'))
Expand Down
16 changes: 7 additions & 9 deletions app/frontend/js/components/ApplicationSidebar.vue
@@ -1,7 +1,11 @@
<template>
<div class="application-sidebar flex h-full bg-white text-white w-56 z-50 border-r border-gray-400">
<div class="flex flex-col w-full">
<router-link to="/" class="logo-placeholder h-16 bg-white p-2 flex justify-center" :active-class="''" exact>
<router-link to="/"
class="logo-placeholder h-16 bg-white p-2 flex justify-center"
:active-class="''"
exact
>
<slot name="logo"/>
</router-link>

Expand All @@ -10,10 +14,10 @@
<sidebar-link to="/"
exact
>
Dashboard
{{ $t('avo.dashboard') }}
</sidebar-link>

<resources-navigation :resources="resources"/>
<resources-navigation />
</div>

<slot name="licensing"/>
Expand All @@ -22,12 +26,6 @@
</div>
</template>

<script>
export default {
props: ['resources'],
}
</script>

<style slang="postcss">
.application-sidebar {
.router-link-active:hover,
Expand Down
8 changes: 4 additions & 4 deletions app/frontend/js/components/AttachModal.vue
Expand Up @@ -13,7 +13,7 @@
v-model="selectedOption"
@keyup.enter="attachOption"
>
<option value="">Choose one</option>
<option value="">{{ $t('avo.choose_an_option') }}</option>
<option v-for="option in options"
:key="option.value"
:value="option.value"
Expand All @@ -29,7 +29,7 @@
:disabled="nothingSelected"
@click="attachOption"
>
Attach
{{ $t('avo.attach') }}
</a-button>
<a-button
ref="attach-another-button"
Expand All @@ -39,14 +39,14 @@
:disabled="nothingSelected"
@click="attachOption(true)"
>
Attach &amp; Attach another
{{ $t('avo.attach_and_attach_another') }}
</a-button>
<a-button
ref="cancel-button"
v-if="attachAction"
@click="$emit('close')"
>
Cancel
{{ $t('avo.cancel') }}
</a-button>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/frontend/js/components/Edit/BelongsTo.vue
Expand Up @@ -31,7 +31,7 @@
<div v-if="searchable">
<a-button color="indigo"
@click="removeSelection"
>Remove selection</a-button>
>{{ $t('avo.remove_selection') }}</a-button>
</div>
</template>
</edit-field-wrapper>
Expand Down
2 changes: 1 addition & 1 deletion app/frontend/js/components/Edit/FileField.vue
Expand Up @@ -13,7 +13,7 @@
<a-button color="indigo"
@click="deleteFile"
v-if="value"
><trash-icon class="h-4 mr-1" /> Delete file</a-button>
><trash-icon class="h-4 mr-1" /> {{ $t('avo.delete_file') }}</a-button>
</template>
</edit-field-wrapper>
</template>
Expand Down
7 changes: 6 additions & 1 deletion app/frontend/js/components/Edit/FilesField.vue
Expand Up @@ -7,7 +7,7 @@
size="xs"
variant="outlined"
color="red"
v-tooltip="`Delete ${file.filename}`"
v-tooltip="deleteTooltipLabel(file)"
@click="deleteFile(file)"
><times-icon class="h-3" /></a-button>
</div>
Expand All @@ -33,6 +33,7 @@

<script>
import { IsFormField } from '@avo-hq/avo-js'
import upperFirst from 'lodash/upperFirst'
export default {
mixins: [IsFormField],
Expand Down Expand Up @@ -68,6 +69,9 @@ export default {
},
},
methods: {
deleteTooltipLabel(file) {
return upperFirst(this.$t('avo.delete_item', { item: file.filename }))
},
deleteFile(file) {
if (file.id) {
this.files = this.files.filter((currentFile) => currentFile.id !== file.id)
Expand All @@ -80,6 +84,7 @@ export default {
this.value = event.target.files
const { files } = event.target
// eslint-disable-next-line no-plusplus
for (let i = 0; i < files.length; i++) {
this.addFile(files.item(i))
}
Expand Down
8 changes: 6 additions & 2 deletions app/frontend/js/components/Edit/HasOne.vue
Expand Up @@ -31,7 +31,8 @@
<div v-if="searchable">
<a-button color="indigo"
@click="removeSelection"
>Remove selection</a-button>
v-text="removeSelectionTooltipLabel"
/>
</div>
</template>
</edit-field-wrapper>
Expand All @@ -57,7 +58,10 @@ export default {
placeholder() {
if (this.field.placeholder) return this.field.placeholder
return `Choose ${this.field.id}`
return this.$t('avo.choose_an_option')
},
removeSelectionTooltipLabel() {
return this.$t('avo.remove_selection')
},
},
methods: {
Expand Down
1 change: 0 additions & 1 deletion app/frontend/js/components/Edit/MarkdownField.vue
Expand Up @@ -12,7 +12,6 @@
v-model="value"
:placeholder="field.placeholder"
language="en"
codeStyle="dracula"
:toolbars="toolbars"
default-open="edit"
:style="{height: field.height}"
Expand Down

0 comments on commit ec224a2

Please sign in to comment.