Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMP] t9n: add custom views to the existing models #3183

Open
wants to merge 3 commits into
base: master-translation-module
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion addons/t9n/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0",
"category": "TODO: find the appropriate category",
"description": "TODO: write a description of the module",
"depends": ["base", "web"],
"depends": ["base", "mail", "web"],
"application": True,
"assets": {
"web.assets_backend": [
Expand Down
9 changes: 9 additions & 0 deletions addons/t9n/models/language.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,12 @@ class Language(models.Model):
_sql_constraints = [
("language_code_unique", "unique(code)", "The language code must be unique.")
]

def _format(self):
return [{
"id": language.id,
"name": language.name,
"code": language.code,
"native_name": language.native_name,
"direction": language.direction,
} for language in self]
32 changes: 17 additions & 15 deletions addons/t9n/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,22 @@ def _check_source_and_target_languages(self):

@api.model
def get_projects(self):
projects_records = self.search([])
return [{
return self.search([])._format()

def _format(self):
return [
{
"id": record.id,
"name": record.name,
"src_lang": {
"id": record.src_lang_id.id,
"name": record.src_lang_id.name if record.src_lang_id.name else "",
},
"resources": [{
"id": resource.id,
"file_name": resource.file_name,
} for resource in record.resource_ids],
"target_langs": [{
"id": lang.id,
"name": lang.name,
} for lang in record.target_lang_ids],
} for record in projects_records]
"src_lang_id": record.src_lang_id._format()[0],
"resource_ids": [
{
"id": resource.id,
"file_name": resource.file_name,
}
for resource in record.resource_ids
],
"target_lang_ids": record.target_lang_ids._format(),
}
for record in self
]
31 changes: 31 additions & 0 deletions addons/t9n/models/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,34 @@ def write(self, vals):
+ [Command.update(id, vals) for id, vals in to_update]
)
return super().write(vals)

@api.model
def get_resources(self, ids):
return self.browse(ids)._format()

def _format(self):
return [
{
"id": resource.id,
"file_name": resource.file_name,
"message_ids": [
{
"id": msg.id,
"body": msg.body,
"translation_ids": [
{
"id": translation.id,
"body": translation.body,
"lang_id": translation.lang_id.id,
}
for translation in msg.translation_ids
],
}
for msg in resource.message_ids
],
"project_id": {
"id": resource.project_id.id,
},
}
for resource in self
]
16 changes: 15 additions & 1 deletion addons/t9n/models/translation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from odoo import fields, models
from odoo import api, fields, models


class Translation(models.Model):
Expand All @@ -18,3 +18,17 @@ class Translation(models.Model):
string="Language",
help="The language to which the translation translates the original message.",
)

@api.model
def create_and_format(self, **kwargs):
return self.create(kwargs)._format()

def _format(self):
return [{
"id": translation.id,
"body": translation.body,
"source_id": {
"id": translation.source_id.id,
},
"lang_id": translation.lang_id._format()[0],
} for translation in self]
18 changes: 16 additions & 2 deletions addons/t9n/static/src/core/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { Component } from "@odoo/owl";
import { Component, useState } from "@odoo/owl";

import { ProjectList } from "@t9n/core/project_list";
import { LanguageList } from "@t9n/core/language_list";
import { ResourceList } from "@t9n/core/resource_list";
import { TranslationEditor } from "@t9n/core/translation_editor";

import { useService } from "@web/core/utils/hooks";

/**
* The "root", the "homepage" of the translation application.
*/
export class App extends Component {
static components = { ProjectList };
static components = { LanguageList, ProjectList, ResourceList, TranslationEditor };
static props = {};
static template = "t9n.App";

setup() {
this.store = useState(useService("mail.store"));
}

get activeView() {
return this.store.t9n.activeView;
}
}
5 changes: 4 additions & 1 deletion addons/t9n/static/src/core/app.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<templates>

<t t-name="t9n.App">
<ProjectList/>
<ProjectList t-if="activeView === 'ProjectList'"/>
<LanguageList t-elif="activeView === 'LanguageList'" languages="store.t9n.activeProject.target_lang_ids"/>
<ResourceList t-elif="activeView === 'ResourceList'" resources="store.t9n.activeProject.resource_ids"/>
<TranslationEditor t-elif="activeView === 'TranslationEditor'"/>
</t>

</templates>
55 changes: 55 additions & 0 deletions addons/t9n/static/src/core/language_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Component, useState } from "@odoo/owl";

import { useService } from "@web/core/utils/hooks";

export class LanguageList extends Component {
static props = { languages: Array };
static template = "t9n.LanguageList";

setup() {
this.action = useService("action");
this.state = useState({
filters: {
searchText: "",
},
sorting: {
column: "name",
order: "asc",
},
});
this.store = useState(useService("mail.store"));
}

get languages() {
const searchTerms = this.state.filters.searchText.trim().toUpperCase();
const languages = searchTerms
? this.props.languages.filter((l) => l.name.toUpperCase().includes(searchTerms))
: [...this.props.languages];
return languages.sort((l1, l2) => {
const l1Col = l1[this.state.sorting.column];
const l2Col = l2[this.state.sorting.column];

if (l1Col < l2Col) {
return this.state.sorting.order === "asc" ? -1 : 1;
}
if (l1Col > l2Col) {
return this.state.sorting.order === "asc" ? 1 : -1;
}
return 0;
});
}

onClickColumnName(column) {
if (this.state.sorting.column === column) {
this.state.sorting.order = this.state.sorting.order === "asc" ? "desc" : "asc";
} else {
this.state.sorting.column = column;
this.state.sorting.order = "asc";
}
}

onClickLanguage(language) {
this.store.t9n.activeView = "ResourceList";
this.store.t9n.activeLanguage = language;
}
}
35 changes: 35 additions & 0 deletions addons/t9n/static/src/core/language_list.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<templates>
<t t-name="t9n.LanguageList">
<div class="container mt-3">
<div class="input-group">
<div class="form-outline">
<input type="text" class="form-control" placeholder="Search languages" t-model="state.filters.searchText"/>
</div>
</div>
<table class="table table-hover">
<thead>
<tr>
<th t-on-click="() => this.onClickColumnName('name')">Language Name <i t-attf-class="btn btn-link fa fa-sort-{{this.state.sorting.column === 'name' and this.state.sorting.order === 'asc' ? 'up' : 'down'}}"></i>
</th>
<th>Native name</th>
<th>Locale</th>
</tr>
</thead>
<tbody>
<t t-foreach="languages" t-as="language" t-key="language.id">
<tr>
<td>
<button class="btn btn-link " t-on-click="() => this.onClickLanguage(language)">
<t t-esc="language.name"/>
</button>
</td>
<td t-esc="language.native_name"/>
<td t-esc="language.code"/>
</tr>
</t>
</tbody>
</table>
</div>
</t>
</templates>
55 changes: 55 additions & 0 deletions addons/t9n/static/src/core/message_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Component, useState } from "@odoo/owl";

import { _t } from "@web/core/l10n/translation";
import { useService } from "@web/core/utils/hooks";

export class MessageForm extends Component {
static props = {};
static template = "t9n.MessageForm";

setup() {
this.state = useState({
suggestedTranslationText: "",
});
this.store = useState(useService("mail.store"));
this.orm = useService("orm");
this.notification = useService("notification");
}

get message() {
return this.store.t9n.activeMessage;
}

get translations() {
return this.message.translationsInCurrentLanguage;
}

onClickClear() {
this.state.suggestedTranslationText = "";
}

async onClickCopy(ev) {
try {
await navigator.clipboard.writeText(this.message.body.trim());
this.notification.add(
_t("Copied to clipboard!"),
{ type: "info" }
);
} catch (error) {
console.error("Error copying text:", error);
}
}

async onClickSuggest() {
const data = await this.orm.call("t9n.translation", "create_and_format", [],
{
body: this.state.suggestedTranslationText.trim(),
source_id: this.store.t9n.activeMessage.id,
lang_id: this.store.t9n.activeLanguage.id,
},
);

this.store["t9n.translation"].insert(data);
this.state.suggestedTranslationText = "";
}
}
70 changes: 70 additions & 0 deletions addons/t9n/static/src/core/message_form.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0"?>
<templates>
<t t-name="t9n.MessageForm">
<div class="card mx-0 border-light">
<div class="card-header">
<t t-esc="message.body"/>
</div>
<div class="card-body">
<div class="card-text">
<p t-if="message.translator_comments">
<span>
<strong>
<em>TRANSLATOR COMMENT </em>
</strong>
</span>
<span>
<t t-esc="message.translator_comments"/>
</span>
</p>
<p t-if="message.extracted_comments">
<span>
<strong>
<em>RESOURCE COMMENT </em>
</strong>
</span>
<span>
<t t-esc="message.extracted_comments"/>
</span>
</p>
<p t-if="message.context">
<span>
<strong>
<em>CONTEXT </em>
</strong>
</span>
<span>

<li class="list-group-item">Cras justo odio</li>
</span>
</p>
<p t-if="message.references">
<span>
<strong>
<em>REFERENCES </em>
</strong>
</span>
<span>
<t t-esc="message.references"/>
</span>
</p>
</div>
<div class="form-group">
<textarea class="form-control" rows="3" placeholder="Write your translation" t-model="state.suggestedTranslationText" ></textarea>
</div>
<div class="d-flex justify-content-end my-3">
<button class="btn btn-outline-primary mx-1" t-on-click="onClickClear">Clear</button>
<button class="btn btn-outline-primary mx-1" t-on-click="onClickCopy">Copy</button>
<button class="btn btn-primary mx-1" t-on-click="onClickSuggest">Suggest</button>
</div>
</div>
</div>
<ul class="list-group">
<t t-foreach="translations" t-as="translation" t-key="translation.id">
<li class="list-group-item">
<t t-esc="translation.body"/>
</li>
</t>
</ul>
</t>
</templates>
14 changes: 14 additions & 0 deletions addons/t9n/static/src/core/models/app_model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Record } from "@mail/core/common/record";

export class AppModel extends Record {
static name = "t9n.App";

activeProject = Record.one("t9n.project");
activeLanguage = Record.one("t9n.language");
activeResource = Record.one("t9n.resource");
activeMessage = Record.one("t9n.message");
/** @type {"ProjectList"|"LanguageList"|"ResourceList"|"TranslationEditor"} */
activeView = "ProjectList";
}

AppModel.register();
Loading