From e3066afe5d2752491271b5fe4b797475ec62ee5a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 2 Dec 2024 13:59:37 +0530 Subject: [PATCH 1/5] feat: added translation plugin --- src/index.js | 1 + src/utils/translation.ts | 59 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/utils/translation.ts diff --git a/src/index.js b/src/index.js index e03a4d21..eab36522 100644 --- a/src/index.js +++ b/src/index.js @@ -100,3 +100,4 @@ export { setConfig, getConfig } from './utils/config.js' export { default as pageMetaPlugin } from './utils/pageMeta.js' export { default as FrappeUI } from './utils/plugin.js' export { confirmDialog } from './utils/confirmDialog.js' +export { default as translationPlugin } from './utils/translation.ts' diff --git a/src/utils/translation.ts b/src/utils/translation.ts new file mode 100644 index 00000000..be1374e7 --- /dev/null +++ b/src/utils/translation.ts @@ -0,0 +1,59 @@ +import { createResource } from '../resources' +import { App } from 'vue' + +declare global { + interface Window { + __: (message: string, replace?: any, context?: string | null) => string + translatedMessages?: { [key: string]: string } + } +} + +export default function translationPlugin(app: App) { + app.config.globalProperties.__ = translate + window.__ = translate + if (!window.translatedMessages) fetchTranslations() +} + +function format(message: string, replace: string) { + return message.replace(/{(\d+)}/g, function (match, number) { + return typeof replace[number] != 'undefined' ? replace[number] : match + }) +} + +function translate( + message: string, + replace?: any, + context: string | null = null, +) { + let translatedMessages = window.translatedMessages || {} + let translatedMessage = '' + + if (context) { + let key = `${message}:${context}` + if (translatedMessages[key]) { + translatedMessage = translatedMessages[key] + } + } + + if (!translatedMessage) { + translatedMessage = translatedMessages[message] || message + } + + const hasPlaceholders = /{\d+}/.test(message) + if (!hasPlaceholders) { + return translatedMessage + } + + return format(translatedMessage, replace) +} + +function fetchTranslations() { + createResource({ + url: 'crm.api.get_translations', + cache: 'translations', + auto: true, + transform: (data: { [key: string]: string }) => { + window.translatedMessages = data + }, + }) +} From f7979ad7f77e43198509aaaac277a1ea0d7b7283 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 2 Dec 2024 14:47:19 +0530 Subject: [PATCH 2/5] fix: also added placeholder __ function if translation plugin is not used --- src/utils/translation.ts | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/utils/translation.ts b/src/utils/translation.ts index be1374e7..ee57105f 100644 --- a/src/utils/translation.ts +++ b/src/utils/translation.ts @@ -1,10 +1,13 @@ import { createResource } from '../resources' import { App } from 'vue' +type Replace = { [key: string]: string } +type TranslatedMessages = Replace + declare global { interface Window { - __: (message: string, replace?: any, context?: string | null) => string - translatedMessages?: { [key: string]: string } + __: (message: string, replace?: Replace, context?: string | null) => string + translatedMessages?: TranslatedMessages } } @@ -14,15 +17,16 @@ export default function translationPlugin(app: App) { if (!window.translatedMessages) fetchTranslations() } -function format(message: string, replace: string) { +function format(message: string, replace?: Replace) { + if (!replace) return message return message.replace(/{(\d+)}/g, function (match, number) { return typeof replace[number] != 'undefined' ? replace[number] : match }) } -function translate( +export function translate( message: string, - replace?: any, + replace?: Replace, context: string | null = null, ) { let translatedMessages = window.translatedMessages || {} @@ -52,8 +56,17 @@ function fetchTranslations() { url: 'crm.api.get_translations', cache: 'translations', auto: true, - transform: (data: { [key: string]: string }) => { - window.translatedMessages = data + transform: (messages: TranslatedMessages) => { + window.translatedMessages = messages }, }) } + +export function __( + message: string, + replace?: Replace, + context: string | null = null, +): string { + if (!window.__) return message + return translate(message, replace, context) +} From 347feba8e6a5f691e80ace99801ef0468c0ebcaf Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 2 Dec 2024 16:35:44 +0530 Subject: [PATCH 3/5] fix: made components translatable --- src/components/Alert.vue | 3 +- src/components/Autocomplete.vue | 15 +++-- src/components/Avatar.vue | 3 +- src/components/Badge.vue | 3 +- src/components/Billing/TrialBanner.vue | 7 +- src/components/Breadcrumbs.vue | 5 +- src/components/Button/Button.vue | 3 +- src/components/Calendar/Calendar.vue | 3 +- src/components/Calendar/CalendarEvent.vue | 5 +- src/components/Calendar/CalendarMonthly.vue | 6 +- src/components/Calendar/EventModalContent.vue | 7 +- .../Calendar/ShowMoreCalendarEvent.vue | 3 +- src/components/Card.vue | 5 +- src/components/Checkbox.vue | 3 +- src/components/CircularProgressBar.vue | 5 +- src/components/ConfirmDialog.vue | 5 +- src/components/DatePicker.vue | 7 +- src/components/DateRangePicker.vue | 7 +- src/components/DateTimePicker.vue | 7 +- src/components/Dialog.vue | 10 +-- src/components/Dropdown.vue | 66 ++++++++++--------- src/components/ErrorMessage.vue | 3 +- src/components/FormControl.vue | 5 +- src/components/Input.vue | 7 +- src/components/ListFilter/ListFilter.vue | 7 +- src/components/ListItem.vue | 5 +- src/components/ListView/ListEmptyState.vue | 7 +- src/components/ListView/ListFooter.vue | 10 ++- src/components/ListView/ListGroupHeader.vue | 3 +- src/components/ListView/ListHeaderItem.vue | 3 +- src/components/ListView/ListRowItem.vue | 3 +- src/components/ListView/ListSelectBanner.vue | 8 +-- src/components/LoadingText.vue | 3 +- src/components/Progress.vue | 5 +- src/components/Rating/Rating.vue | 3 +- src/components/Select.vue | 5 +- src/components/Switch.vue | 9 +-- src/components/TabButtons.vue | 4 +- src/components/Tabs.vue | 3 +- .../TextEditor/TextEditorFloatingMenu.vue | 3 +- src/components/TextInput.vue | 3 +- src/components/Textarea.vue | 5 +- src/components/Toast.vue | 5 +- src/components/Tooltip/Tooltip.vue | 3 +- src/components/Tree/Tree.vue | 3 +- 45 files changed, 174 insertions(+), 119 deletions(-) diff --git a/src/components/Alert.vue b/src/components/Alert.vue index 227ffd3c..12f6e2e8 100644 --- a/src/components/Alert.vue +++ b/src/components/Alert.vue @@ -22,7 +22,7 @@

- {{ title }} + {{ __(title) }}

@@ -37,6 +37,7 @@ diff --git a/src/components/LoadingText.vue b/src/components/LoadingText.vue index 2fb0f829..0521a0fa 100644 --- a/src/components/LoadingText.vue +++ b/src/components/LoadingText.vue @@ -1,9 +1,10 @@ diff --git a/src/components/ConfirmDialog.vue b/src/components/ConfirmDialog.vue index 78b1016e..bf0372b6 100644 --- a/src/components/ConfirmDialog.vue +++ b/src/components/ConfirmDialog.vue @@ -2,7 +2,11 @@ diff --git a/src/components/Input.vue b/src/components/Input.vue index 63d54827..6428e1ad 100644 --- a/src/components/Input.vue +++ b/src/components/Input.vue @@ -141,6 +141,9 @@ export default { }, emits: ['input', 'change', 'update:modelValue'], methods: { + __(message) { + return __(message) + }, focus() { this.$refs.input.focus() }, diff --git a/src/components/ListItem.vue b/src/components/ListItem.vue index b8418715..12a38e2b 100644 --- a/src/components/ListItem.vue +++ b/src/components/ListItem.vue @@ -14,16 +14,23 @@
- diff --git a/src/components/LoadingText.vue b/src/components/LoadingText.vue index 0521a0fa..a54e049c 100644 --- a/src/components/LoadingText.vue +++ b/src/components/LoadingText.vue @@ -3,20 +3,14 @@ {{ __(text) }}
- diff --git a/src/components/TabButtons.vue b/src/components/TabButtons.vue index 86d33f4b..7f3bdc38 100644 --- a/src/components/TabButtons.vue +++ b/src/components/TabButtons.vue @@ -36,38 +36,27 @@
- diff --git a/src/components/TextEditor/TextEditorFloatingMenu.vue b/src/components/TextEditor/TextEditorFloatingMenu.vue index 9a38df1d..6dfae545 100644 --- a/src/components/TextEditor/TextEditorFloatingMenu.vue +++ b/src/components/TextEditor/TextEditorFloatingMenu.vue @@ -24,37 +24,39 @@ - diff --git a/src/components/Toast.vue b/src/components/Toast.vue index b997d614..16b8fcc4 100644 --- a/src/components/Toast.vue +++ b/src/components/Toast.vue @@ -15,7 +15,11 @@ > {{ __(title) }}

-

+

@@ -31,52 +35,41 @@
- From 0a4bfdf2b65d770d25fa69852b1fb6e7f1129a2e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 2 Dec 2024 17:49:49 +0530 Subject: [PATCH 5/5] fix: updated get translations api --- src/utils/translation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/translation.ts b/src/utils/translation.ts index ee57105f..a46b26c9 100644 --- a/src/utils/translation.ts +++ b/src/utils/translation.ts @@ -53,7 +53,7 @@ export function translate( function fetchTranslations() { createResource({ - url: 'crm.api.get_translations', + url: 'frappe.translate.get_app_translations', cache: 'translations', auto: true, transform: (messages: TranslatedMessages) => {