Add translation support for DocFields (#5002)

* Initial changes for Translation

* Merged translate app into frappe core

* Minor typo fix

* Fixes while syncing translations

* Set defaults

* Fixes on setup

* Improve Code Quality

* Could Code Quality be Improved?

* Small code tweaks

* Minor typo fix

* Addition of Translatable Property

* Small fixes for Codacy Comments

* Simplify code

* Show translations in grid

* Remove enabled from Language

* Revert render_template logic

* typo

* Cleanup unused methods

* move patch to erpnext

* fix codacy

* Update patches.txt

* Update patches.txt
This commit is contained in:
Faris Ansari 2018-02-16 15:12:12 +05:30 committed by Rushabh Mehta
parent 5f8c9978b7
commit e581be9fa8
20 changed files with 1873 additions and 1481 deletions

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@ from frappe.custom.doctype.property_setter.property_setter import make_property_
from frappe.desk.notifications import delete_notification_count_for
from frappe.modules import make_boilerplate
from frappe.model.db_schema import validate_column_name, validate_column_length
from frappe.model.docfield import supports_translation
import frappe.website.render
# imports - third-party imports
@ -56,6 +57,7 @@ class DocType(Document):
self.scrub_field_names()
self.set_default_in_list_view()
self.set_default_translatable()
self.validate_series()
self.validate_document_type()
validate_fields(self)
@ -88,6 +90,12 @@ class DocType(Document):
cnt += 1
if cnt == 4: break
def set_default_translatable(self):
'''Ensure that non-translatable never will be translatable'''
for d in self.fields:
if d.translatable and not supports_translation(d.fieldtype):
d.translatable = 0
def check_developer_mode(self):
"""Throw exception if not developer mode or via patch"""
if frappe.flags.in_patch or frappe.flags.in_test:

View file

@ -1,174 +1,174 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:language_code",
"beta": 0,
"creation": "2014-08-22 16:12:17.249590",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "language_code",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Language Code",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "language_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Language Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "flag",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Flag",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "based_on",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Based On",
"length": 0,
"no_copy": 0,
"options": "Language",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-globe",
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-29 14:40:33.210645",
"modified_by": "Administrator",
"module": "Core",
"name": "Language",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "language_name",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "language_name",
"track_changes": 1,
"track_seen": 0
}
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:language_code",
"beta": 0,
"creation": "2014-08-22 16:12:17.249590",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "language_code",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Language Code",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "language_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Language Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "flag",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Flag",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "based_on",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Based On",
"length": 0,
"no_copy": 0,
"options": "Language",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-globe",
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-29 14:40:33.210645",
"modified_by": "Administrator",
"module": "Core",
"name": "Language",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "language_name",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "language_name",
"track_changes": 1,
"track_seen": 0
}

View file

@ -43,6 +43,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -74,6 +75,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -104,6 +106,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -135,6 +138,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -168,6 +172,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -197,6 +202,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -230,6 +236,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -263,6 +270,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -294,6 +302,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -324,6 +333,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -353,6 +363,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -384,6 +395,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -415,6 +427,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -446,6 +459,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -475,6 +489,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -507,6 +522,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "300px"
},
@ -540,6 +556,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -571,6 +588,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -602,6 +620,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -633,6 +652,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50%"
},
@ -665,6 +685,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -695,6 +716,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -724,6 +746,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -754,6 +777,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -783,6 +807,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -814,6 +839,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -845,6 +871,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -874,6 +901,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -905,6 +933,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -936,6 +965,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -965,6 +995,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -995,6 +1026,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1026,6 +1058,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1056,6 +1089,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1087,6 +1121,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1116,6 +1151,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1147,6 +1183,40 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
"fieldname": "translatable",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Translatable",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],

View file

@ -7,6 +7,7 @@ import json
from frappe.utils import cstr
from frappe import _
from frappe.model.document import Document
from frappe.model.docfield import supports_translation
class CustomField(Document):
def autoname(self):
@ -39,6 +40,9 @@ class CustomField(Document):
if not self.fieldname:
frappe.throw(_("Fieldname not set for Custom Field"))
if self.get('translatable', 0) and not supports_translation(self.fieldtype):
self.translatable = 0
if not self.flags.ignore_validate:
from frappe.core.doctype.doctype.doctype import check_if_fieldname_conflicts_with_methods
check_if_fieldname_conflicts_with_methods(self.dt, self.fieldname)

View file

@ -13,6 +13,7 @@ from frappe.utils import cint
from frappe.model.document import Document
from frappe.model import no_value_fields
from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype
from frappe.model.docfield import supports_translation
doctype_properties = {
'search_fields': 'Data',
@ -53,6 +54,7 @@ docfield_properties = {
'print_hide_if_no_value': 'Check',
'report_hide': 'Check',
'allow_on_submit': 'Check',
'translatable': 'Check',
'depends_on': 'Data',
'description': 'Text',
'default': 'Text',
@ -210,6 +212,10 @@ class CustomizeForm(Document):
frappe.msgprint(_("You can't set 'Options' for field {0}").format(df.label))
continue
elif property == 'translatable' and not supports_translation(df.get('fieldtype')):
frappe.msgprint(_("You can't set 'Translatable' for field {0}").format(df.label))
continue
self.make_property_setter(property=property, value=df.get(property),
property_type=docfield_properties[property], fieldname=df.fieldname)

View file

@ -40,6 +40,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -71,6 +72,7 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -104,6 +106,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -135,6 +138,7 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -167,6 +171,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px"
},
@ -198,6 +203,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -227,6 +233,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -257,6 +264,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -288,6 +296,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -318,6 +327,40 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)",
"fieldname": "translatable",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Translatable",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -347,6 +390,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -380,6 +424,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -411,6 +456,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -443,6 +489,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -473,6 +520,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -505,6 +553,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -537,6 +586,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -569,6 +619,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px"
},
@ -600,6 +651,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -631,6 +683,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -662,6 +715,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -693,6 +747,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -722,6 +777,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -751,6 +807,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -782,6 +839,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -813,6 +871,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -844,6 +903,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -874,6 +934,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -905,6 +966,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -937,6 +999,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px"
},
@ -967,6 +1030,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -999,6 +1063,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "300px"
},
@ -1031,6 +1096,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1062,6 +1128,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1093,6 +1160,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px"
},
@ -1126,6 +1194,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@ -1158,6 +1227,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50px"
},
@ -1189,6 +1259,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -1202,7 +1273,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-10-24 11:41:31.075929",
"modified": "2018-01-10 11:08:17.294691",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form Field",

View file

@ -12,10 +12,10 @@ def rename(doctype, fieldname, newname):
(doctype, fieldname), as_dict=1)
if not df:
return
df = df[0]
if frappe.db.get_value('DocType', doctype, 'issingle'):
if frappe.db.get_value('DocType', doctype, 'issingle'):
update_single(df, newname)
else:
update_table(df, newname)
@ -33,7 +33,7 @@ def update_table(f, new):
query = get_change_column_query(f, new)
if query:
frappe.db.sql(query)
def update_parent_field(f, new):
"""update 'parentfield' in tables"""
if f['fieldtype']=='Table':
@ -41,11 +41,14 @@ def update_parent_field(f, new):
frappe.db.sql("""update `tab%s` set parentfield=%s where parentfield=%s""" \
% (f['options'], '%s', '%s'), (new, f['fieldname']))
frappe.db.commit()
def get_change_column_query(f, new):
"""generate change fieldname query"""
desc = frappe.db.sql("desc `tab%s`" % f['parent'])
for d in desc:
if d[0]== f['fieldname']:
return 'alter table `tab%s` change `%s` `%s` %s' % \
(f['parent'], f['fieldname'], new, d[1])
(f['parent'], f['fieldname'], new, d[1])
def supports_translation(fieldtype):
return fieldtype in ["Data", "Select", "Text", "Small Text", "Text Editor"]

View file

@ -229,6 +229,15 @@ class Meta(Document):
return title_field
def get_translatable_fields(self):
'''Return all fields that are translation enabled'''
return [d.fieldname for d in self.fields if d.translatable]
def is_translatable(self, fieldname):
'''Return true of false given a field'''
field = self.get_field(fieldname)
return field and field.translatable
def process(self):
# don't process for special doctypes
# prevent's circular dependency

View file

@ -203,4 +203,4 @@ execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True)
frappe.patches.v10_0.reload_countries_and_currencies
frappe.patches.v10_0.refactor_social_login_keys
frappe.patches.v10_0.enable_chat_by_default_within_system_settings
frappe.patches.v10_0.remove_custom_field_for_disabled_domain
frappe.patches.v10_0.remove_custom_field_for_disabled_domain

View file

@ -238,6 +238,7 @@
"public/js/frappe/ui/toolbar/modules_select.js",
"public/js/frappe/ui/toolbar/notifications.js",
"public/js/frappe/views/communication.js",
"public/js/frappe/views/translation_manager.js",
"public/js/frappe/ui/sort_selector.html",
"public/js/frappe/ui/sort_selector.js",

View file

@ -94,6 +94,43 @@ frappe.ui.form.Control = Class.extend({
&& this.$wrapper.toggleClass("hide-control", this.disp_status=="None")
&& this.refresh_input
&& this.refresh_input();
var value = this.get_value();
this.show_translatable_button(value);
},
show_translatable_button(value) {
// Disable translation non-string fields or special string fields
if (!frappe.model.can_write('Translation')
|| !this.frm
|| !this.doc
|| !this.df.translatable
|| !value) return;
// Disable translation in website
if (!frappe.views || !frappe.views.TranslationManager) return;
// Already attached button
if (this.$wrapper.find('.clearfix .btn-translation').length) return;
const translation_btn =
`<a class="btn-translation no-decoration text-muted" title="${__('Open Translation')}">
<i class="fa fa-globe"></i>
</a>`;
$(translation_btn)
.appendTo(this.$wrapper.find('.clearfix'))
.on('click', () => {
if (!this.doc.__islocal) {
new frappe.views.TranslationManager({
'df': this.df,
'source_name': value,
'target_language': this.doc.language,
'doc': this.doc
});
}
});
},
get_doc: function() {
return this.doctype && this.docname

View file

@ -86,11 +86,22 @@ frappe.ui.form.GridRow = Class.extend({
console.trace(e); // eslint-disable-line
});
} else {
this.grid.df.data = this.grid.df.data.filter(function(d) {
return d.name !== me.doc.name;
})
let data = null;
if (this.grid.df.get_data) {
data = this.grid.df.get_data();
} else {
data = this.grid.df.data;
}
const index = data.findIndex(d => d.name === me.doc.name);
if (index > -1) {
// mutate array directly,
// else the object reference will be lost
data.splice(index, 1);
}
// remap idxs
this.grid.df.data.forEach(function(d, i) {
data.forEach(function(d, i) {
d.idx = i+1;
});

View file

@ -25,4 +25,4 @@ frappe.get_languages = function() {
frappe.languages = frappe.languages.sort(function(a, b) { return (a.value < b.value) ? -1 : 1 });
}
return frappe.languages;
};
};

View file

@ -198,11 +198,12 @@ frappe.views.CommunicationComposer = Class.extend({
method: 'frappe.email.doctype.standard_reply.standard_reply.get_standard_reply',
args: {
template_name: standard_reply,
doc: me.frm.doc
doc: me.frm.doc,
_lang: me.dialog.get_value("language_sel")
},
callback: function(r) {
prepend_reply(r.message);
}
},
});
}
},

View file

@ -0,0 +1,108 @@
frappe.views.TranslationManager = class TranslationManager {
constructor(opts) {
Object.assign(this, opts);
this.make();
}
make() {
this.data = [];
this.dialog = new frappe.ui.Dialog({
fields: this.get_fields(),
title: __('Translate {0}', [this.df.label]),
no_submit_on_enter: true,
primary_action_label: __('Update Translations'),
primary_action:
(values) => this.update_translations(values)
.then(() => {
this.dialog.hide();
this.data = [];
frappe.msgprint({
title: __('Success'),
message: __('Successfully updated translations'),
indicator: 'green'
});
})
});
this.get_translations_data()
.then(data => {
this.data.push(...(data || []));
this.dialog.refresh();
this.dialog.show();
});
}
get_fields() {
var fields = [
{
label: __('Source Text'),
fieldname: 'source',
fieldtype: 'Data',
read_only: 1,
bold: 1,
default: this.source_name
},
{
label: __('Translations'),
fieldname: 'translation_data',
fieldtype: 'Table',
fields: [
{
label: 'Language',
fieldname: 'language',
fieldtype: 'Link',
options: 'Language',
in_list_view: 1,
columns: 3
},
{
label: 'Translation',
fieldname: 'translation',
fieldtype: 'Data',
in_list_view: 1,
columns: 7
}
],
data: this.data,
get_data: () => {
return this.data;
}
}
];
return fields;
}
get_translations_data() {
return frappe.db.get_list('Translation', {
fields: ['name', 'language', 'target_name as translation'],
filters: {
source_name: this.source_name
}
});
}
update_translations({ source, translation_data = [] }) {
const translation_dict = {};
translation_data.map(row => {
translation_dict[row.language] = row.translation;
});
return frappe.call({
method: 'frappe.translate.update_translations_for_source',
btn: this.dialog.get_primary_btn(),
args: {
source,
translation_dict
}
}).fail(() => {
frappe.msgprint({
title: __('Something went wrong'),
message: __('Please try again'),
indicator: 'red'
});
});
}
};

View file

@ -85,7 +85,7 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
<div class="col-xs-{{ "3" if df.fieldtype=="Check" else "7" }}
{{ get_align_class(df) }} value">
{% if doc.get(df.fieldname) != None -%}
{{ print_value(df, doc) }}{% endif %}
{{ _(print_value(df, doc)) }}{% endif %}
</div>
</div>
{%- endmacro -%}
@ -97,7 +97,7 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
{%- if df.fieldtype=="Code" %}
<pre class="value">{{ doc.get(df.fieldname) }}</pre>
{% else -%}
{{ doc.get_formatted(df.fieldname, parent_doc or doc) }}
{{ doc.get_formatted(df.fieldname, parent_doc or doc, translated=df.translatable) }}
{% endif -%}
</div>
{%- endif -%}
@ -131,7 +131,7 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
{% elif df.fieldtype=="HTML" %}
{{ frappe.render_template(df.options, {"doc":doc}) }}
{% else %}
{{ doc.get_formatted(df.fieldname, parent_doc or doc, translated=True) }}
{{ doc.get_formatted(df.fieldname, parent_doc or doc, translated=df.translatable) }}
{% endif %}
{%- endmacro %}

View file

@ -390,6 +390,7 @@ def get_messages_from_workflow(doctype=None, app_name=None):
return messages
def get_messages_from_custom_fields(app_name):
fixtures = frappe.get_hooks('fixtures', app_name=app_name) or []
custom_fields = []
@ -701,3 +702,32 @@ def rename_language(old_name, new_name):
frappe.db.sql("""update `tabUser` set language=%(new_name)s where language=%(old_name)s""",
{ "old_name": old_name, "new_name": new_name })
@frappe.whitelist()
def update_translations_for_source(source=None, translation_dict=None):
if not (source and translation_dict):
return
translation_dict = json.loads(translation_dict)
# for existing records
translation_records = frappe.db.get_values('Translation', { 'source_name': source }, ['name', 'language'], as_dict=1)
for d in translation_records:
if translation_dict.get(d.language, None):
doc = frappe.get_doc('Translation', d.name)
doc.target_name = translation_dict.get(d.language)
doc.save()
# done with this lang value
translation_dict.pop(d.language)
else:
frappe.delete_doc('Translation', d.name)
# remaining values are to be inserted
for lang, target_name in iteritems(translation_dict):
doc = frappe.new_doc('Translation')
doc.language = lang
doc.source_name = source
doc.target_name = target_name
doc.save()
return translation_records

View file

@ -53,6 +53,7 @@ def install_basic_docs():
'roles': [{'role': 'Guest'}]
},
{'doctype': "Role", "role_name": "Report Manager"},
{'doctype': "Role", "role_name": "Translator"},
{'doctype': "Workflow State", "workflow_state_name": "Pending",
"icon": "question-sign", "style": ""},
{'doctype': "Workflow State", "workflow_state_name": "Approved",

View file

@ -172,7 +172,7 @@ def convert_markdown(doc, meta):
@frappe.whitelist()
def get_html_and_style(doc, name=None, print_format=None, meta=None,
no_letterhead=None, trigger_print=False, style=None):
no_letterhead=None, trigger_print=False, style=None, lang=None):
"""Returns `html` and `style` of print format, used in PDF etc"""
if isinstance(doc, string_types) and isinstance(name, string_types):