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 @@
- {{ text }}
+ {{ __(text) }}
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) => {