Skip to content

Conversation

@bvisible
Copy link
Contributor

@bvisible bvisible commented Jan 1, 2026

Summary

Components translated:

  • Settings components: Canned responses, Email settings, SLA, Teams, Holidays, etc.
  • Modal components: MergeCategoryModal, TicketSplitModal, CustomerDialog, CallLogDetailModal, etc.
  • Page components: Contacts, Customers, CallLogs, Articles, KnowledgeBase, Ticket pages
  • Utility components: Autocomplete, EmptyState, CommunicationArea, TextEditor, EmailEditor
  • Ticket components: TicketAgentActivities, TicketAgentDetails, TicketAgentFields, TicketBreadcrumbs
  • View controls: Filter.vue (operators, timespan options), SortBy.vue
  • Telephony: CallUI, TwilioCallUI, ExotelCallUI

Translation pattern used:

import { __ } from "@/translation";

// Simple strings
__("Filter")

// String interpolation
__("{0} matches", [count])
__("Due in {0}", [formatTime(seconds)])

Test plan

  • Build passes without errors (yarn build)
  • Verify translations work correctly with a non-English locale
  • Check that interpolated strings display correctly

@codecov
Copy link

codecov bot commented Jan 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 39.68%. Comparing base (90c365a) to head (9f87a52).
⚠️ Report is 9 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #2856   +/-   ##
========================================
  Coverage    39.68%   39.68%           
========================================
  Files          121      121           
  Lines         4926     4926           
========================================
  Hits          1955     1955           
  Misses        2971     2971           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Assignment Rules: Added __ import and translated ticketRoutingOptions
- EmailNotifications: Added __ import to Notification.vue
- FieldDependency: Translated all user-facing strings in criteria,
  fields selection, and value selection components
- Holiday: Full translation of calendar, list, table view, modals,
  and recurring holidays components

This is part of the effort to add i18n support across the codebase
as requested in PR frappe#2836.
- SLA: Translated all modals (EditResponseResolution, WorkDay),
  list items, priorities, status, workdays, and assignment conditions
- Agents: Translated role labels, dropdown options, toast messages
- AgentCard: Translated inactive badge
- Email: Translated config fields, labels, descriptions, validation
  messages, and service info texts
- InviteAgents: Translated role labels, toast messages
- EmailAccountList: Added __ import
- General: Added __ import to LogoUpload and WorkflowKnowledgebaseSettings
- Teams: Translated validation messages in NewTeam, button labels in
  TeamEdit and RenameTeamModal
- AssignmentModal: Translated title and placeholder
- ConfirmDialog: Translated confirm button label
- ViewModal: Translated title, placeholder, and button labels
- TicketMergeModal: Translated all strings and messages
- TicketSplitModal: Translated all strings and messages
- CategoryModal: Translated title and action button labels
- NewCustomerDialog: Translated title, labels, and messages
- MergeCategoryModal.vue
- MoveToCategoryModal.vue
- TicketSubjectModal.vue
- AddNewAgentsDialog.vue
- CustomerDialog.vue
- CallLogDetailModal.vue
- SettingsModal.vue (add missing import)
- InvalidPage.vue
- Contacts.vue
- Customers.vue
- CallLogs.vue
- Articles.vue
- KnowledgeBaseCustomer.vue
- TicketConversation.vue
- TicketAgent.vue
- TicketCustomerTemplateFields.vue
- TicketTextEditor.vue
- TicketBreadcrumbs.vue
- Autocomplete.vue
- EmptyState.vue
- CommunicationArea.vue
- TextEditor.vue
- EmailEditor.vue
@bvisible bvisible force-pushed the feat/i18n-vue-translations branch from d39943b to 848eec1 Compare January 1, 2026 16:22
@bvisible bvisible force-pushed the feat/i18n-vue-translations branch from 848eec1 to 610b3c6 Compare January 1, 2026 16:31
@RitvikSardana
Copy link
Member

@bvisible

are you using AI for it?

if yes can you share the prompt?

@bvisible
Copy link
Contributor Author

bvisible commented Jan 2, 2026

Hi @RitvikSardana 👋

Yes! For tedious and repetitive tasks like wrapping 500+ strings across 100+ files with translation functions, AI is a huge time saver.

I'm using Claude Code (Anthropic's CLI tool) with a multi-agent workflow:

3-Agent Workflow:

  1. Scanner Agent - Analyzes the codebase to find all hardcoded strings that need translation, generates a detailed markdown plan listing each file, line numbers, and strings to wrap with __()

  2. Modifier Agent - Takes the plan and systematically applies the translations using the __() function from @/translation, handling string interpolation patterns like __("{0} items", [count])

  3. Verifier Agent - Reviews all modifications to ensure:

    • Import statement import { __ } from "@/translation" is present
    • All user-facing strings are wrapped
    • No syntax errors introduced
    • Prettier formatting is correct

This approach ensures consistency and catches edge cases like template literals, computed properties, and nested strings.


📋 Click to see the full Claude Code prompt

Vue i18n Translation Workflow

You will perform internationalization (i18n) on Vue.js files using a 3-phase approach.

Phase 1: Scanner Agent

Scan the codebase to identify all Vue files with hardcoded user-facing strings.

Task:

  1. Search all .vue files in src/ directory
  2. Identify hardcoded strings in:
    • Template: labels, placeholders, titles, messages, button text
    • Script: toast messages, error messages, dialog titles/messages
  3. Generate a markdown report with:
    • File path
    • Line numbers
    • Current hardcoded string
    • Suggested translation wrapper

Output format:

## Files to translate

### src/components/Example.vue
- Line 15: `label="Submit"``:label="__('Submit')"` 
- Line 42: `toast.success("Saved!")``toast.success(__("Saved!"))`

Phase 2: Modifier Agent

Apply translations systematically based on the scan report.

Rules:

  1. Add import if missing: import { __ } from "@/translation";
  2. Wrap strings with __() function
  3. For interpolation use: __("{0} items found", [count])
  4. For template literals: __("Hello {0}", [name])
  5. Skip: technical strings, CSS classes, event names, prop names
  6. Preserve existing formatting

Examples:

// Before
<Button label="Save" />
toast.error("Failed to load data");
const title = `Hello ${name}`;

// After  
<Button :label="__('Save')" />
toast.error(__("Failed to load data"));
const title = __("Hello {0}", [name]);

Phase 3: Verifier Agent

Verify all modifications are correct.

Checklist:

  • Import statement present in each modified file
  • All user-facing strings wrapped
  • No duplicate imports
  • Syntax is valid (run yarn build)
  • Prettier formatting applied (npx [email protected] --write)
  • No technical strings accidentally wrapped

Run build to verify: yarn build
Run prettier: npx [email protected] --write "src/**/*.vue"

@RitvikSardana
Copy link
Member

Hello seems like a few translations are pending @bvisible

bvisible and others added 3 commits January 13, 2026 10:59
- Tickets.vue: Translate 'Create' button
- Sidebar.vue: Translate 'Frappe Helpdesk' title
- CallLogModal.vue: Translate phone placeholders
- CommunicationArea.vue: Translate email/comment placeholders
- NewCustomerDialog.vue: Translate example placeholders
- CustomerDialog.vue: Translate domain placeholder
- ContactDialog.vue: Translate email/phone placeholders
- SearchComplete.vue: Translate 'Select an option' placeholder
Comment on lines 43 to 57
label: __("Default Incoming"),
name: "default_incoming",
type: "checkbox",
description:
"If enabled, all replies to your company (eg: [email protected]) will come to this account. Note: Only one account can be default incoming.",
description: __(
"If enabled, all replies to your company (eg: [email protected]) will come to this account. Note: Only one account can be default incoming."
),
},
{
label: "Default Outgoing",
label: __("Default Outgoing"),
name: "default_outgoing",
type: "checkbox",
description:
"If enabled, all outgoing emails will be sent from this account. Note: Only one account can be default outgoing.",
description: __(
"If enabled, all outgoing emails will be sent from this account. Note: Only one account can be default outgoing."
),
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of editing the label here, better to translate it in EmailAdd or EmailEdit component

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - reverted in 663bc51

Comment on lines 14 to 20
label: __("Account Name"),
name: "email_account_name",
type: "text",
placeholder: "Support / Sales",
},
{
label: "Email ID",
label: __("Email ID"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translate the message inside the UI only, not here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - reverted in 663bc51

});
};

const columns = computed(() => [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to do it in UI

{{ __(column.label) }} => line 21

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - moved translation to template line 21 in 663bc51

});
if (group.group.label == "General") {
_actions = _actions.filter((action) => action.label === "Add New Article");
if (group.group.label == __("General")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never do this, comparing with translated string is always wrong

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - removed translated string comparison in 663bc51

@@ -14,9 +14,9 @@
<span>{{ group.group.label }}</span>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add translations here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - added translation in 663bc51

}
if (!Object.keys(groups).length) {
groups["No results"] = [];
groups[__("No results")] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - reverted in 663bc51


withDefaults(defineProps<Props>(), {
title: "No Data Found",
title: "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?
can revert it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - reverted in 663bc51

Comment on lines 12 to 22
const {
label = "Discard",
hideDialog = false,
title = "Discard?",
message = "Are you sure you want to discard this?",
} = defineProps<{
label?: string;
hideDialog?: boolean;
title?: string;
message?: string;
}>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why have you removed these defaults?

Please revert this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - reverted in 663bc51

@RitvikSardana
Copy link
Member

RitvikSardana commented Jan 13, 2026

Please add translations in

  • Column Settings

and other components

@bvisible

- Revert emailConfig.ts: translate in UI, not config
- Revert IconPicker.vue: don't use translated string as object key
- Revert EmptyState.vue: keep original default handling
- Revert DiscardButton.vue: keep original defaults
- Fix ListRows.vue: don't compare against translated strings
- Fix SlaPriorityList.vue: translate in template, not data
- Fix savedReplies.ts: remove translations from config
- Add translations to ColumnSettings.vue
- Reload.vue: Translate 'Refresh'
- MobileSidebar.vue: Translate 'Notifications', 'Dashboard'
- CommentTextEditor.vue: Translate 'Discard'
@bvisible
Copy link
Contributor Author

Added translations to ColumnSettings.vue in 663bc51 and more components (Reload, MobileSidebar, CommentTextEditor) in da5cd69.

- Telephony.vue: translate form labels and section headers
- AssignmentRuleView.vue: translate button labels, toast messages and dialog
- CFCondition.vue and CFConditions.vue: translate button labels
- MultiSelectCombobox.vue: translate Select All and Clear buttons
- EmptyState.vue: translate Create and No Data Found
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants