This project holds the source code for the translation platform web application built with Owl and Vite. It is developed in Typescript and styled using Tailwind
- Clone this repository and install dependencies with
npm install
- Create a
.env.local
environment file based on the.env
example one - Launch the Vite development server with
npm run dev
- Access it on
http://localhost:3000
- Create a
.env.production.local
environment file based on the.env.local
example one and fill it - Run
npm run build
, it will build static files in the/dist
directory - Copy those files wherever you want
Please read the vite documentation. Mainly, environment files are loaded based on their name given the running mode:
- running
npm run dev
will load the.env.local
file - running
npm run build
will first load.env.local
and then the.env.production.local
file overriding any values
The .local
part of the filename indicates that it must not be commited.
The application in itself is built with Owl as reactive framework. Owl offers various useful primitives such as a component structure, data bindings and a template engine, but no ready-to-use components. Thus multiple components have been developed to address that, including but not limited to:
- A frontend router working with the Javascript History
- A central store built with Odoo's primitives
- A transition component to mount and unmount components according to CSS transitions
- Multiple reusable visual components (Modal, Table, Button...)
The code is divided in the following directories and "main" files:
/src/main.ts
is the entry point, it loads Owl and mounts the layout component/src/icons.ts
lists and loads the various icons from FontAwesome/src/notifications.ts
defines a notification object based on Notyf to display small notifications to the user/src/routes.ts
declares the various routes and navigation guards of the application/src/store.ts
defines the central store/public
contains the various static files such as pictures/src/components
contains the various reusable components and components shared by multiple pages/src/hooks
contains a few useful hooks to use in components/src/models
contains the API abstractions and DAOs used by the components to get and update data/src/pages
contains the page components, mounted by the router/src/i18n
contains translations related stuff
Note that each file contains documentation about what it does, as such each component is documented in the file where it is defined.
The structure of the application makes it easier to define a template as close as possible
to its component definition. The Owl way of doing it is be setting an inline string in the static
template
field of a component class. It is nice for small components but once it grows it becomes
harder to maintain, furthermore we have much less developer tooling (unable to recognize it is XML).
Thus a small vite plugin is set in /vite.config.js
to load XML files as Odoo templates. It is thus
possible to do the following:
import { Component } from '@odoo/owl';
import componentTemplate from './template.xml'; // important to give the extension
class MyComponent extends Component {
static template = componentTemplate;
// ...
}
Note that the plugin will automatically pass the content of the XML file in Owl's xml
template string
function.
Translations are, as much as possible, handled using Owl's internal translation function, it is
defined in /src/i18n/index.ts
. Simply put the translation method defined will attempt to replace
any string given by Owl, and fallback to what's provided, which should be in english.
Defining new languages is done by first creating a file for it (take example on /src/i18n/fr.ts
)
and translating the various strings found in it. You can then register it in the index.ts
file under
dictionnaries with the minified lang representation as key.
You can then register your new language in /src/components/SettingsModal.ts
in the languages
field
of the component.
Whenever a missing translation is found it will be logged to the browser's console. you can easily dump
the various missing translations by running dumpMissingTranslations()
in your browser console, which will
log a JSON object containing them.
When running the platform in dev environment, you will very probably run into a cross-origin requests
problem. To fix it quick and dirty, edit the /odoo/service/wsgi_server.py
in Odoo's source code.
Patch the application_unproxied
function like so:
def application_unproxied(environ, start_response):
""" WSGI entry point."""
# cleanup db/uid trackers - they're set at HTTP dispatch in
# web.session.OpenERPSession.send() and at RPC dispatch in
# odoo.service.web_services.objects_proxy.dispatch().
# /!\ The cleanup cannot be done at the end of this `application`
# method because werkzeug still produces relevant logging afterwards
if hasattr(threading.current_thread(), 'uid'):
del threading.current_thread().uid
if hasattr(threading.current_thread(), 'dbname'):
del threading.current_thread().dbname
if hasattr(threading.current_thread(), 'url'):
del threading.current_thread().url
if environ['REQUEST_METHOD'] == "OPTIONS":
response = werkzeug.wrappers.Response('OPTIONS METHOD DETECTED')
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response.headers['Access-Control-Max-Age'] = 1000
# note that '*' is not valid for Access-Control-Allow-Headers
response.headers['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
return response(environ, start_response)
with odoo.api.Environment.manage():
result = odoo.http.root(environ, start_response)
if result is not None:
return result
# We never returned from the loop.
return werkzeug.exceptions.NotFound("No handler found.\n")(environ, start_response)
Then you also have to update the /odoo/addons/base/controllers/rpc.py
, update both xmlrpc/2/<service>
and /xmlrpc/<service>
route params
@route("/xmlrpc/<service>", auth="none", methods=["POST"], csrf=False, save_session=False, cors='*')
@route("/xmlrpc/2/<service>", auth="none", methods=["POST"], csrf=False, save_session=False, cors='*')
By adding the cors="*"
parameter.
Note that this only tested with Odoo 12 and 14 and responds accordingly to the preflight Option request.