1+ <% content_for :page_title, t('.title') %>
2+
3+ <div class ="container-fluid " data-controller ="better-together--tabs better-together--translation ">
4+ < div class ="row ">
5+ < div class ="col-12 ">
6+ < h1 class ="h2 mb-4 ">
7+ < i class ="fa-solid fa-language me-2 " aria-hidden ="true "> </ i >
8+ <%= t ( '.title' ) %>
9+ </ h1 >
10+
11+ <% if @translated_model_types . any? %>
12+ <%
13+ # Get current locale filter from params
14+ selected_locale = params [ :locale_filter ] || 'all'
15+ available_locales = I18n . available_locales . map ( &:to_s )
16+
17+ # Group model types by namespace
18+ grouped_models = @translated_model_types . group_by do |model_type |
19+ parts = model_type . split ( '::' )
20+ parts . length > 1 ? parts [ 0 ..-2 ] . join ( '::' ) : 'Base'
21+ end
22+
23+ # Sort groups with 'BetterTogether' first, then 'Base', then alphabetically
24+ sorted_groups = grouped_models . sort_by do |namespace , _ |
25+ case namespace
26+ when 'BetterTogether' then '0'
27+ when 'Base' then 'z'
28+ else namespace
29+ end
30+ end
31+ %>
32+
33+ <!-- Locale Filter Tabs -->
34+ < div class ="mb-4 ">
35+ < h3 class ="h5 mb-3 ">
36+ < i class ="fa-solid fa-filter me-2 " aria-hidden ="true "> </ i >
37+ Filter by Locale
38+ </ h3 >
39+ < ul class ="nav nav-pills " id ="localeFilterTabs " role ="tablist ">
40+ < li class ="nav-item " role ="presentation ">
41+ <%= link_to better_together . translations_path ( locale_filter : 'all' ) ,
42+ class : "nav-link #{ 'active' if selected_locale == 'all' } " ,
43+ id : "all-locales-tab" ,
44+ role : "tab" ,
45+ 'aria-selected' : selected_locale == 'all' ,
46+ data : {
47+ 'better-together--translation-target' : 'tab' ,
48+ locale : 'all'
49+ } do %>
50+ < i class ="fa-solid fa-globe me-2 " aria-hidden ="true "> </ i >
51+ All Locales
52+ < span class ="badge bg-secondary ms-2 "> <%= @translated_model_types . count %> models</ span >
53+ <% end %>
54+ </ li >
55+ <% available_locales . each do |locale | %>
56+ <%
57+ # Calculate total translation percentage for this locale across all models
58+ if @translation_stats &.any?
59+ total_records = @translation_stats . values . sum { |model_stats | model_stats [ locale ] &.dig ( :total ) || 0 }
60+ total_translated = @translation_stats . values . sum { |model_stats | model_stats [ locale ] &.dig ( :translated ) || 0 }
61+ locale_percentage = total_records > 0 ? ( ( total_translated . to_f / total_records ) * 100 ) . round ( 1 ) : 0
62+ else
63+ locale_percentage = 0
64+ total_translated = 0
65+ end
66+ %>
67+ < li class ="nav-item " role ="presentation ">
68+ <%= link_to better_together . translations_path ( locale_filter : locale ) ,
69+ class : "nav-link #{ 'active' if selected_locale == locale } " ,
70+ id : "#{ locale } -locale-tab" ,
71+ role : "tab" ,
72+ 'aria-selected' : selected_locale == locale ,
73+ data : {
74+ 'better-together--translation-target' : 'tab' ,
75+ locale : locale
76+ } do %>
77+ < i class ="fa-solid fa-language me-2 " aria-hidden ="true "> </ i >
78+ <%= t ( "locales.#{ locale } " ) %>
79+ < span class ="badge bg-<%= locale_percentage >= 80 ? 'success' : locale_percentage >= 50 ? 'warning' : 'danger' %> ms-2 ">
80+ <%= locale_percentage %> % (<%= total_translated %> )
81+ </ span >
82+ <% end %>
83+ </ li >
84+ <% end %>
85+ </ ul >
86+ </ div >
87+
88+ <% if selected_locale != 'all' %>
89+ < div class ="alert alert-info mb-4 " role ="alert ">
90+ < i class ="fa-solid fa-info-circle me-2 " aria-hidden ="true "> </ i >
91+ Showing translation data filtered for: < strong > <%= t ( "locales.#{ selected_locale } " ) %> </ strong >
92+ <%= link_to "Show all locales" , better_together . translations_path , class : "btn btn-sm btn-outline-primary ms-2" %>
93+ </ div >
94+ <% end %>
95+
96+ <% sorted_groups . each_with_index do |( namespace , models ) , group_index | %>
97+ < div class ="mb-4 ">
98+ < h2 class ="h4 mb-3 ">
99+ < i class ="fa-solid fa-folder me-2 " aria-hidden ="true "> </ i >
100+ <%= namespace == 'Base' ? 'Core Models' : namespace . humanize %>
101+ < span class ="badge bg-secondary ms-2 "> <%= models . count %> </ span >
102+ </ h2 >
103+
104+ <!-- Navigation Tabs for this namespace -->
105+ < ul class ="nav nav-tabs " id ="translationsTab <%= group_index %> " role ="tablist ">
106+ <% models . each_with_index do |model_type , model_index | %>
107+ <%
108+ model_class = model_type . constantize
109+ tab_id = "#{ model_type . downcase . gsub ( '::' , '-' ) } -tab"
110+ panel_id = "##{ model_type . downcase . gsub ( '::' , '-' ) } -panel"
111+ is_active = group_index == 0 && model_index == 0
112+
113+ # Extract the class name without namespace for display
114+ class_name = model_type . split ( '::' ) . last
115+ %>
116+ < li class ="nav-item " role ="presentation ">
117+ < button class ="nav-link <%= ' active' if is_active %> "
118+ id ="<%= tab_id %> "
119+ data-bs-toggle ="tab "
120+ data-bs-target ="<%= panel_id %> "
121+ href ="<%= panel_id %> "
122+ type ="button "
123+ role ="tab "
124+ aria-controls ="<%= panel_id . sub ( '#' , '' ) %> "
125+ aria-selected ="<%= is_active %> "
126+ data-better-together--tabs-target ="tab ">
127+ < i class ="fa-solid fa-file-text me-2 " aria-hidden ="true "> </ i >
128+ <%= class_name . humanize %>
129+ <% if namespace != 'Base' && namespace != 'BetterTogether' %>
130+ < small class ="text-muted ms-1 "> (<%= namespace . split ( '::' ) . last %> )</ small >
131+ <% end %>
132+ </ button >
133+ </ li >
134+ <% end %>
135+ </ ul >
136+
137+ <!-- Tab Content for this namespace -->
138+ < div class ="tab-content mt-3 mb-4 " id ="translationsTabContent <%= group_index %> ">
139+ <% models . each_with_index do |model_type , model_index | %>
140+ <%
141+ model_class = model_type . constantize
142+ panel_id = "#{ model_type . downcase . gsub ( '::' , '-' ) } -panel"
143+ tab_id = "#{ model_type . downcase . gsub ( '::' , '-' ) } -tab"
144+ is_active = group_index == 0 && model_index == 0
145+
146+ class_name = model_type . split ( '::' ) . last
147+ %>
148+ < div class ="tab-pane fade <%= ' show active' if is_active %> "
149+ id ="<%= panel_id %> "
150+ role ="tabpanel "
151+ aria-labelledby ="<%= tab_id %> "
152+ tabindex ="0 ">
153+ < div class ="card ">
154+ < div class ="card-header d-flex justify-content-between align-items-center ">
155+ < h3 class ="card-title h5 mb-0 ">
156+ < i class ="fa-solid fa-translate me-2 " aria-hidden ="true "> </ i >
157+ <%= model_class . model_name . human %>
158+ < small class ="text-muted ms-2 "> (<%= model_type %> )</ small >
159+ </ h3 >
160+ <% if selected_locale != 'all' %>
161+ < span class ="badge bg-info ">
162+ < i class ="fa-solid fa-filter me-1 " aria-hidden ="true "> </ i >
163+ <%= t ( "locales.#{ selected_locale } " ) %> Only
164+ </ span >
165+ <% end %>
166+ </ div >
167+ < div class ="card-body ">
168+ <%
169+ translated_attributes = model_class . respond_to? ( :mobility_attributes ) ? model_class . mobility_attributes : [ ]
170+ %>
171+
172+ <% if translated_attributes . any? %>
173+ < div class ="mb-4 ">
174+ < h6 class ="text-muted mb-3 ">
175+ < i class ="fa-solid fa-table me-2 " aria-hidden ="true "> </ i >
176+ Translation Values for <%= model_class . model_name . human %>
177+ </ h6 >
178+
179+ < div class ="table-responsive ">
180+ < table class ="table table-striped table-hover ">
181+ < thead class ="table-dark ">
182+ < tr >
183+ < th scope ="col " class ="text-nowrap ">
184+ < i class ="fa-solid fa-key me-2 " aria-hidden ="true "> </ i >
185+ ID
186+ </ th >
187+ < th scope ="col " class ="text-nowrap ">
188+ < i class ="fa-solid fa-tag me-2 " aria-hidden ="true "> </ i >
189+ Identifier
190+ </ th >
191+ <% translated_attributes . each do |attribute | %>
192+ < th scope ="col " class ="text-nowrap ">
193+ < i class ="fa-solid fa-language me-2 " aria-hidden ="true "> </ i >
194+ <%= attribute . to_s . humanize %>
195+ </ th >
196+ <% end %>
197+ < th scope ="col " class ="text-nowrap ">
198+ < i class ="fa-solid fa-cog me-2 " aria-hidden ="true "> </ i >
199+ Actions
200+ </ th >
201+ </ tr >
202+ </ thead >
203+ < tbody >
204+ < tr >
205+ < td colspan ="<%= translated_attributes . count + 3 %> " class ="text-center text-muted py-4 ">
206+ < i class ="fa-solid fa-info-circle me-2 " aria-hidden ="true "> </ i >
207+ No <%= model_class . model_name . human . downcase %> records to display.
208+ Translation data will appear here when available.
209+ </ td >
210+ </ tr >
211+ </ tbody >
212+ </ table >
213+ </ div >
214+ </ div >
215+
216+ < div class ="row ">
217+ < div class ="col-md-6 ">
218+ < h6 class ="text-muted "> Translated Attributes:</ h6 >
219+ < ul class ="list-group list-group-flush ">
220+ <% translated_attributes . each do |attribute | %>
221+ < li class ="list-group-item d-flex justify-content-between align-items-center px-0 ">
222+ < span >
223+ < i class ="fa-solid fa-language me-2 text-primary " aria-hidden ="true "> </ i >
224+ < code > <%= attribute %> </ code >
225+ </ span >
226+ < span class ="badge bg-secondary rounded-pill ">
227+ <%= attribute . to_s . humanize . downcase %>
228+ </ span >
229+ </ li >
230+ <% end %>
231+ </ ul >
232+ </ div >
233+
234+ < div class ="col-md-6 ">
235+ < h6 class ="text-muted "> Model Information:</ h6 >
236+ < ul class ="list-unstyled small text-muted ">
237+ < li > < strong > Full Class:</ strong > < code > <%= model_type %> </ code > </ li >
238+ < li > < strong > Namespace:</ strong > < code > <%= namespace %> </ code > </ li >
239+ < li > < strong > Model Name:</ strong > < code > <%= class_name %> </ code > </ li >
240+ < li > < strong > Attributes Count:</ strong > <%= translated_attributes . count %> </ li >
241+ </ ul >
242+
243+ < div class ="mt-3 ">
244+ < small class ="text-muted ">
245+ < i class ="fa-solid fa-info-circle me-1 " aria-hidden ="true "> </ i >
246+ These attributes support multiple language translations through the Mobility gem.
247+ </ small >
248+ </ div >
249+ </ div >
250+ </ div >
251+ <% else %>
252+ < div class ="alert alert-warning " role ="alert ">
253+ < i class ="fa-solid fa-exclamation-triangle me-2 " aria-hidden ="true "> </ i >
254+ No translated attributes found for this model.
255+ </ div >
256+
257+ < div class ="mt-3 ">
258+ < h6 class ="text-muted "> Model Information:</ h6 >
259+ < ul class ="list-unstyled small text-muted ">
260+ < li > < strong > Full Class:</ strong > < code > <%= model_type %> </ code > </ li >
261+ < li > < strong > Namespace:</ strong > < code > <%= namespace %> </ code > </ li >
262+ < li > < strong > Model Name:</ strong > < code > <%= class_name %> </ code > </ li >
263+ </ ul >
264+ </ div >
265+ <% end %>
266+ </ div >
267+ </ div >
268+ </ div >
269+ <% end %>
270+ </ div >
271+ </ div >
272+ <% end %>
273+ <% else %>
274+ < div class ="alert alert-info " role ="alert ">
275+ < i class ="fa-solid fa-info-circle me-2 " aria-hidden ="true "> </ i >
276+ <%= t ( '.no_translatable_content' ) %>
277+ </ div >
278+ <% end %>
279+ </ div >
280+ </ div >
281+ </ div >
0 commit comments