-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "Translations", | ||
"version": "1.0", | ||
"category": "TODO: find the appropriate category", | ||
"description": "TODO: write a description of the module", | ||
"depends": ["base", "web"], | ||
"application": True, | ||
"assets": { | ||
"web.assets_backend": [ | ||
"t9n/static/src/**/*", | ||
], | ||
}, | ||
"data": [ | ||
"data/t9n.language.csv", | ||
"security/ir.model.access.csv", | ||
"views/t9n_project_views.xml", | ||
"views/t9n_menu_views.xml", | ||
"views/t9n_language_views.xml", | ||
"views/t9n_resource_views.xml", | ||
"views/t9n_message_views.xml", | ||
], | ||
"license": "LGPL-3", | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from . import language | ||
from . import message | ||
from . import project | ||
from . import resource | ||
from . import translation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from odoo import fields, models | ||
|
||
|
||
class Language(models.Model): | ||
_name = "t9n.language" | ||
_description = "Language" | ||
|
||
name = fields.Char("Formal Name", required=True, readonly=True) | ||
code = fields.Char("Code", required=True, readonly=True) | ||
native_name = fields.Char("Native Name", readonly=True) | ||
direction = fields.Selection( | ||
required=True, | ||
selection=[ | ||
("ltr", "left-to-right"), | ||
("rtl", "right-to-left"), | ||
], | ||
readonly=True, | ||
) | ||
|
||
_sql_constraints = [ | ||
("language_code_unique", "unique(code)", "The language code must be unique.") | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from odoo import api, fields, models | ||
|
||
|
||
class Message(models.Model): | ||
"""Models a localizable message, i.e. any textual content to be translated. | ||
Messages are retrieved from a Resource. | ||
A Message localized to a specific Language becomes a Translation. | ||
""" | ||
|
||
_name = "t9n.message" | ||
_description = "Localizable message" | ||
|
||
body = fields.Text( | ||
"Entry to be Translated", | ||
help="Text to Translate", | ||
) | ||
context = fields.Char(help="Text Context") | ||
translator_comments = fields.Text( | ||
help="Comments written by the translator/developer in the resource file.", | ||
) | ||
extracted_comments = fields.Text("Resource Comments") | ||
references = fields.Text( | ||
help="The full text that represents the references, one per line.", | ||
) | ||
resource_id = fields.Many2one( | ||
comodel_name="t9n.resource", | ||
help="The resource (typically a file) from which the entry is coming from.", | ||
ondelete="cascade", | ||
required=True, | ||
) | ||
translation_ids = fields.One2many( | ||
comodel_name="t9n.translation", | ||
inverse_name="source_id", | ||
string="Translations", | ||
) | ||
|
||
_sql_constraints = [ | ||
( | ||
"body_context_resource_unique", | ||
"UNIQUE(body, context, resource_id)", | ||
"The combination of a text to translate and its context must be unique within the same resource!", | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from odoo import fields, models, api, _ | ||
from odoo.exceptions import ValidationError | ||
|
||
|
||
class Project(models.Model): | ||
"""A project is a collection of Resources to be localized into a given set | ||
of Languages. | ||
""" | ||
|
||
_name = "t9n.project" | ||
_description = "Translation project" | ||
|
||
name = fields.Char("Project", required=True) | ||
src_lang_id = fields.Many2one( | ||
comodel_name="t9n.language", | ||
string="Source Language", | ||
help="The original language of the messages you want to translate.", | ||
) | ||
resource_ids = fields.One2many( | ||
comodel_name="t9n.resource", | ||
inverse_name="project_id", | ||
string="Resources", | ||
) | ||
target_lang_ids = fields.Many2many( | ||
comodel_name="t9n.language", | ||
string="Languages", | ||
help="The list of languages into which the project can be translated.", | ||
) | ||
|
||
@api.constrains("src_lang_id", "target_lang_ids") | ||
def _check_source_and_target_languages(self): | ||
for record in self: | ||
if record.src_lang_id in record.target_lang_ids: | ||
raise ValidationError(_("A project's target languages must be different from its source language.")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import base64 | ||
|
||
import polib | ||
|
||
from odoo import Command, _, api, fields, models | ||
from odoo.exceptions import UserError, ValidationError | ||
from odoo.tools import format_list | ||
|
||
|
||
class Resource(models.Model): | ||
_name = "t9n.resource" | ||
_description = "Resource file" | ||
|
||
file_name = fields.Char() | ||
file = fields.Binary("Resource File", store=False) | ||
message_ids = fields.One2many( | ||
comodel_name="t9n.message", | ||
inverse_name="resource_id", | ||
string="Entries to translate", | ||
) | ||
project_id = fields.Many2one( | ||
comodel_name="t9n.project", | ||
) | ||
|
||
_sql_constraints = [ | ||
( | ||
"file_name_project_id_unique", | ||
"unique(file_name, project_id)", | ||
"A file with the same name already exists in the same project.", | ||
), | ||
] | ||
|
||
def _decode_resource_file(self, resource_file): | ||
try: | ||
file_content = base64.b64decode(resource_file).decode() | ||
po_obj = polib.pofile(file_content) | ||
except (IOError, UnicodeDecodeError): | ||
po_obj = [] | ||
return [ | ||
{ | ||
"body": entry.msgid, | ||
"context": entry.msgctxt, | ||
"translator_comments": entry.tcomment, | ||
"extracted_comments": entry.comment, | ||
"references": "\n".join([fpath + (lineno and f":{lineno}") for fpath, lineno in entry.occurrences]), | ||
} | ||
for entry in po_obj | ||
] | ||
|
||
@api.model_create_multi | ||
def create(self, vals_list): | ||
broken_files = [] | ||
for vals in vals_list: | ||
if not vals.get("file"): | ||
raise ValidationError(_("A resource file is required to create a resource.")) | ||
po_obj = self._decode_resource_file(vals["file"]) | ||
del vals["file"] | ||
if not po_obj: | ||
broken_files.append(vals["file_name"]) | ||
continue | ||
vals["message_ids"] = [Command.create(message) for message in po_obj] | ||
if broken_files: | ||
raise UserError( | ||
_( | ||
"Resource files must be valid .pot files. The following files are ill-formatted or empty: %(file_names)s", | ||
file_names=format_list(self.env, broken_files), | ||
), | ||
) | ||
return super().create(vals_list) | ||
|
||
def write(self, vals): | ||
self.ensure_one() | ||
if "file" not in vals: | ||
return super().write(vals) | ||
po_obj = self._decode_resource_file(vals["file"]) | ||
del vals["file"] | ||
if not po_obj: | ||
raise UserError( | ||
_("The files: %(file_name)s should be a .po file with a valid syntax.", file_name=vals["file_name"]), | ||
) | ||
current_msgs_by_tuple = {(msg.body, msg.context): msg for msg in self.message_ids} | ||
new_msgs_by_tuple = {(msg["body"], msg["context"]): msg for msg in po_obj} | ||
to_create = [msg_val for key, msg_val in new_msgs_by_tuple.items() if key not in current_msgs_by_tuple] | ||
to_unlink = {msg.id for key, msg in current_msgs_by_tuple.items() if key not in new_msgs_by_tuple} | ||
to_update = [ | ||
(current_msgs_by_tuple[key].id, new_msgs_by_tuple[key]) | ||
for key in set(current_msgs_by_tuple) & set(new_msgs_by_tuple) | ||
] | ||
vals["message_ids"] = ( | ||
[Command.create(vals) for vals in to_create] | ||
+ [Command.unlink(id) for id in to_unlink] | ||
+ [Command.update(id, vals) for id, vals in to_update] | ||
) | ||
return super().write(vals) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from odoo import fields, models | ||
|
||
|
||
class Translation(models.Model): | ||
_name = "t9n.translation" | ||
_description = "Message translated into a language" | ||
|
||
body = fields.Text( | ||
help="The actual content of the translation.", | ||
) | ||
source_id = fields.Many2one( | ||
comodel_name="t9n.message", | ||
string="Source message", | ||
help="The original text, the source of the translation.", | ||
) | ||
lang_id = fields.Many2one( | ||
comodel_name="t9n.language", | ||
string="Language", | ||
help="The language to which the translation translates the original message.", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||
access_t9n_project_system,t9n.project.system,t9n.model_t9n_project,base.group_system,1,1,1,1 | ||
access_t9n_language_system,t9n.language.system,t9n.model_t9n_language,base.group_system,1,1,1,1 | ||
access_t9n_message_system,t9n.message.system,t9n.model_t9n_message,base.group_system,1,1,1,1 | ||
access_t9n_resource_system,t9n.resource.system,t9n.model_t9n_resource,base.group_system,1,1,1,1 | ||
access_t9n_translation_system,t9n.translation.system,t9n.model_t9n_translation,base.group_system,1,1,1,1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Component } from "@odoo/owl"; | ||
|
||
/** | ||
* The "root", the "homepage" of the translation application. | ||
*/ | ||
export class App extends Component { | ||
static components = {}; | ||
static props = {}; | ||
static template = "t9n.App"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0"?> | ||
<templates xml:space="preserve"> | ||
|
||
<t t-name="t9n.App"> | ||
Hello World! | ||
</t> | ||
|
||
</templates> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Component, xml } from "@odoo/owl"; | ||
|
||
import { App } from "@t9n/core/app"; | ||
|
||
import { registry } from "@web/core/registry"; | ||
import { standardActionServiceProps } from "@web/webclient/actions/action_service"; | ||
|
||
/** | ||
* Wraps the application root, allowing us to open the application as a result | ||
* of a call to the "t9n.open_app" client action. | ||
*/ | ||
export class OpenApp extends Component { | ||
static components = { App }; | ||
static props = { ...standardActionServiceProps }; | ||
static template = xml`<App/>`; | ||
} | ||
|
||
registry.category("actions").add("t9n.open_app", OpenApp); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?xml version="1.0"?> | ||
<odoo> | ||
<record id="t9n.language_view_list" model="ir.ui.view"> | ||
<field name="name">t9n.language.list</field> | ||
<field name="model">t9n.language</field> | ||
<field name="arch" type="xml"> | ||
<tree create="false" edit="false"> | ||
<field name="code"/> | ||
<field name="name"/> | ||
<field name="native_name"/> | ||
</tree> | ||
</field> | ||
</record> | ||
<record id="t9n.language_view_search" model="ir.ui.view"> | ||
<field name="name">t9n.language.search</field> | ||
<field name="model">t9n.language</field> | ||
<field name="arch" type="xml"> | ||
<search> | ||
<field name="name" string="All Languages" filter_domain="[ '|', '|', ('name', 'ilike', self), ('native_name', 'ilike', self), ('code', 'ilike', self)]"/> | ||
<field name="name"/> | ||
<field name="native_name"/> | ||
<field name="code"/> | ||
</search> | ||
</field> | ||
</record> | ||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0"?> | ||
<odoo> | ||
<record id="t9n.open_app" model="ir.actions.client"> | ||
<field name="name">Translate</field> | ||
<field name="tag">t9n.open_app</field> | ||
<field name="target">main</field> | ||
</record> | ||
<menuitem id="t9n.menu_root" name="Translations" action="t9n.open_app"/> | ||
<menuitem id="t9n.project_menu" name="Projects" action="t9n.project_action" parent="t9n.menu_root"/> | ||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?xml version="1.0"?> | ||
<odoo> | ||
<record id="t9n.message_view_list" model="ir.ui.view"> | ||
<field name="name">t9n.message.list</field> | ||
<field name="model">t9n.message</field> | ||
<field name="arch" type="xml"> | ||
<tree create="false"> | ||
<field name="body"/> | ||
</tree> | ||
</field> | ||
</record> | ||
|
||
<record id="t9n.message_view_form" model="ir.ui.view"> | ||
<field name="name">t9n.message.form</field> | ||
<field name="model">t9n.message</field> | ||
<field name="arch" type="xml"> | ||
<form> | ||
<sheet> | ||
<field name="body"/> | ||
<group> | ||
<field name="extracted_comments"/> | ||
<field name="translator_comments"/> | ||
</group> | ||
<group> | ||
<field name="context"/> | ||
</group> | ||
<notebook> | ||
<page string="References"> | ||
<field name="references"/> | ||
</page> | ||
<page string="Translations"> | ||
<field name="translation_ids"/> | ||
</page> | ||
</notebook> | ||
</sheet> | ||
</form> | ||
</field> | ||
</record> | ||
</odoo> |
Oops, something went wrong.