i18n_feedback 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a89bcc5c9f18d206d9aeeaf42aab087c528e0746828637af04839cc3adcb96b0
4
- data.tar.gz: ddcb6df38749afa84e7aa2c5b0cb0a249b5dc1ec7ffca359d134d6e1dc529610
3
+ metadata.gz: 8c389e87f5f47b445f94f6ea242e803159bb34b2e0847d2ccc87883d05b3a666
4
+ data.tar.gz: 62ef3573c3d1a2070b151bbd77848e6f3c2db2c3db2abe532b6c32f0e8afb78f
5
5
  SHA512:
6
- metadata.gz: 323a02d3eb15547900b837d8778b21e5a57b1816e3fd021e74650ad6a4b0e487c56c8f0658f6920a76474f70309f4a68304f0545f0d4c1ce245d138566c29ba9
7
- data.tar.gz: 1a28aa0bc4f392e28fc65a4ed8edeeae6a20acd37deb74a398b810a20e4d9a9e054911610458b1168d2499a7e4be4abd269ac695daada9fdfea525d9878f3617
6
+ metadata.gz: 67b4232a078ccb30b36de48123023151659301f6036bbac5069ea46db65dabc094cdfff3f3bd6267d63efd43f98b2aaed6c7912d384fdff1da8979f5788c738c
7
+ data.tar.gz: 8f575349159537f6ee689d6abea8397ad425bfca692822c1ae93286934fa36bc8edba846b6a65572c5caf74e0fa6ce5ee752d29ad5e0c7df800329992355cae3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.3.0]
6
+
7
+ - Localize the widget's own UI. Every string in the pill and the suggestion
8
+ popover now resolves through Rails I18n under the `i18n_feedback.*` scope and
9
+ follows the app's current `I18n.locale`, so the tool speaks the same language as
10
+ the app being proofread. Any key a host hasn't translated falls back to English
11
+ — so nothing goes blank in a locale you haven't fully covered. Override any
12
+ string by defining the matching key in your own locale files.
13
+ - Ship translations out of the box for 20 languages in addition to English:
14
+ Arabic, Bengali, Chinese (Simplified), Dutch, French, German, Hindi, Indonesian,
15
+ Italian, Japanese, Korean, Polish, Portuguese, Russian, Spanish, Thai, Turkish,
16
+ Ukrainian, Urdu and Vietnamese.
17
+ - Render the popover right-to-left for RTL locales (Arabic, Urdu, and other RTL
18
+ scripts), detected from the active locale's language subtag. The i18n key stays
19
+ left-to-right, since it's a code identifier rather than prose.
20
+ - `config.pill_label` now defaults to `nil`, meaning "use the localized default".
21
+ Setting it to a string still overrides the pill text as before.
22
+ - The widget now follows the operating system's light/dark/system appearance via
23
+ `prefers-color-scheme`. The pill and popover render with a dark surface when the
24
+ reviewer's system is in dark mode, with no configuration required.
25
+
5
26
  ## [0.2.2]
6
27
 
7
28
  - Fix suggest mode desyncing under a nonce-based CSP on Turbo visits. The runtime
data/README.md CHANGED
@@ -177,6 +177,49 @@ Set `config.auto_inject = false` and drop the helper at the end of your layout:
177
177
 
178
178
  It renders nothing unless the tool is available for the request.
179
179
 
180
+ ### Localizing the widget UI
181
+
182
+ The pill and the suggestion popover speak the app's language: every string
183
+ resolves through Rails I18n under the `i18n_feedback.*` scope and follows the
184
+ current `I18n.locale`. Translations ship out of the box for English plus 20 more
185
+ languages — Arabic, Bengali, Chinese (Simplified), Dutch, French, German, Hindi,
186
+ Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Russian, Spanish,
187
+ Thai, Turkish, Ukrainian, Urdu and Vietnamese — so the tool is already localized
188
+ for most apps. RTL locales (Arabic, Urdu, …) render the popover right-to-left
189
+ automatically.
190
+
191
+ Any key you haven't translated falls back to English, so a partially translated
192
+ locale never leaves a control blank. To add a language, or reword the bundled
193
+ copy, define the keys in your own locale files (yours win over the gem's):
194
+
195
+ ```yaml
196
+ # config/locales/fr.yml
197
+ fr:
198
+ i18n_feedback:
199
+ pill: "Proposer des corrections"
200
+ pill_active: "En cours — appuyez pour quitter (Échap)"
201
+ title: "Proposer une correction de traduction"
202
+ current_text: "Texte actuel"
203
+ suggested_text: "Texte proposé"
204
+ comment: "Commentaire"
205
+ comment_placeholder: "Note facultative pour le développeur"
206
+ prior_title: "Déjà proposé (en attente)"
207
+ cancel: "Annuler"
208
+ save: "Envoyer la suggestion"
209
+ error_blank: "Veuillez saisir une suggestion."
210
+ error_save: "Impossible d'enregistrer la suggestion."
211
+ ```
212
+
213
+ `config.pill_label` still overrides the pill text with a fixed string if you set
214
+ it; leave it `nil` (the default) to use the localized `i18n_feedback.pill` key.
215
+
216
+ ### Light / dark / system appearance
217
+
218
+ The widget follows the reviewer's operating-system appearance via
219
+ `prefers-color-scheme` — no configuration needed. The pill and popover render on a
220
+ dark surface when the system is in dark mode and a light surface otherwise; the
221
+ blue accent stays the same in both.
222
+
180
223
  ## Reviewing suggestions
181
224
 
182
225
  Suggestions are ordinary records:
@@ -158,13 +158,19 @@
158
158
  var pill = document.createElement("button");
159
159
  pill.type = "button";
160
160
  pill.className = "i18nf-pill" + (config.active ? " i18nf-pill-on" : "");
161
- pill.textContent = config.active ? "✓ Suggesting — tap to exit (Esc)" : "✎ " + labelText();
161
+ pill.textContent = config.active ? "✓ " + t("pillActive", "Suggesting — tap to exit (Esc)") : "✎ " + labelText();
162
162
  pill.addEventListener("click", toggle);
163
163
  document.body.appendChild(pill);
164
164
  }
165
165
 
166
166
  function labelText() {
167
- return (config.pillLabel || "Suggest edits");
167
+ return config.pillLabel || t("pill", "Suggest edits");
168
+ }
169
+
170
+ // Resolve a server-translated label, falling back to English if the config
171
+ // predates the labels payload (e.g. a cached page from an older gem version).
172
+ function t(name, fallback) {
173
+ return (config.labels && config.labels[name]) || fallback;
168
174
  }
169
175
 
170
176
  // --- popover --------------------------------------------------------------
@@ -178,18 +184,21 @@
178
184
  });
179
185
 
180
186
  var panel = el("div", "i18nf-panel");
187
+ // Mirror the popover for right-to-left locales (Arabic, Urdu, …). The i18n
188
+ // key stays LTR — it's a code identifier, not prose (see heading()).
189
+ panel.dir = config.rtl ? "rtl" : "ltr";
181
190
  panel.appendChild(heading(key));
182
191
 
183
192
  priorNode = el("div");
184
193
  panel.appendChild(priorNode);
185
194
 
186
- panel.appendChild(field("Current text", readonly(currentValue)));
195
+ panel.appendChild(field(t("currentText", "Current text"), readonly(currentValue)));
187
196
 
188
197
  proposedInput = textarea(currentValue);
189
- panel.appendChild(field("Suggested text", proposedInput));
198
+ panel.appendChild(field(t("suggestedText", "Suggested text"), proposedInput));
190
199
 
191
- commentInput = input("Optional note for the developer");
192
- panel.appendChild(field("Comment", commentInput));
200
+ commentInput = input(t("commentPlaceholder", "Optional note for the developer"));
201
+ panel.appendChild(field(t("comment", "Comment"), commentInput));
193
202
 
194
203
  errorNode = el("p", "i18nf-error");
195
204
  errorNode.style.display = "none";
@@ -229,7 +238,7 @@
229
238
 
230
239
  var box = el("div", "i18nf-prior");
231
240
  var title = el("p", "i18nf-prior-title");
232
- title.textContent = "Already suggested (pending)";
241
+ title.textContent = t("priorTitle", "Already suggested (pending)");
233
242
  box.appendChild(title);
234
243
 
235
244
  var list = el("ul", "i18nf-prior-list");
@@ -250,12 +259,12 @@
250
259
 
251
260
  var cancel = el("button", "i18nf-btn i18nf-btn-ghost");
252
261
  cancel.type = "button";
253
- cancel.textContent = "Cancel";
262
+ cancel.textContent = t("cancel", "Cancel");
254
263
  cancel.addEventListener("click", close);
255
264
 
256
265
  saveButton = el("button", "i18nf-btn i18nf-btn-primary");
257
266
  saveButton.type = "button";
258
- saveButton.textContent = "Send suggestion";
267
+ saveButton.textContent = t("save", "Send suggestion");
259
268
  saveButton.addEventListener("click", function () {
260
269
  submit(key, currentValue);
261
270
  });
@@ -268,7 +277,7 @@
268
277
  function submit(key, currentValue) {
269
278
  var proposed = proposedInput.value.trim();
270
279
  if (!proposed) {
271
- showError("Please enter a suggestion.");
280
+ showError(t("errorBlank", "Please enter a suggestion."));
272
281
  return;
273
282
  }
274
283
 
@@ -297,7 +306,7 @@
297
306
  close();
298
307
  } else {
299
308
  saveButton.disabled = false;
300
- showError("Could not save the suggestion.");
309
+ showError(t("errorSave", "Could not save the suggestion."));
301
310
  }
302
311
  })
303
312
  .catch(function () {
@@ -316,8 +325,9 @@
316
325
  function heading(key) {
317
326
  var wrapper = el("div", "i18nf-heading");
318
327
  var title = el("p", "i18nf-title");
319
- title.textContent = "Suggest a translation fix";
328
+ title.textContent = t("title", "Suggest a translation fix");
320
329
  var code = el("code", "i18nf-key");
330
+ code.dir = "ltr"; // the key is a code path, never RTL prose
321
331
  code.textContent = key;
322
332
  wrapper.appendChild(title);
323
333
  wrapper.appendChild(code);
@@ -405,6 +415,28 @@
405
415
  ".i18nf-btn-ghost { background: transparent; color: #111; }",
406
416
  ".i18nf-btn-primary { background: #2563eb; color: #fff; }",
407
417
  ".i18nf-btn[disabled] { opacity: .6; cursor: default; }",
418
+
419
+ // Follow the OS light/dark/system setting via prefers-color-scheme, so the
420
+ // widget matches whatever the reviewer's system is set to without any extra
421
+ // configuration. Only the surfaces that carry their own background/color
422
+ // above are overridden here — the blue accents stay the same in both themes.
423
+ "@media (prefers-color-scheme: dark) {",
424
+ " .i18nf-pill { background: #1f1f23; color: #f4f4f5; border-color: rgba(255,255,255,.18);",
425
+ " box-shadow: 0 4px 14px rgba(0,0,0,.5); }",
426
+ " .i18nf-pill-on { background: #2563eb; color: #fff; border-color: #2563eb; }",
427
+ " .i18nf-overlay { background: rgba(0,0,0,.65); }",
428
+ " .i18nf-panel { background: #1f1f23; color: #f4f4f5;",
429
+ " box-shadow: 0 20px 60px rgba(0,0,0,.7); }",
430
+ " .i18nf-key { color: #a1a1aa; }",
431
+ " .i18nf-label { color: #a1a1aa; }",
432
+ " .i18nf-readonly { background: #2a2a30; }",
433
+ " .i18nf-input { background: #2a2a30; color: #f4f4f5; border-color: #3f3f46; }",
434
+ " .i18nf-input::placeholder { color: #71717a; }",
435
+ " .i18nf-prior { background: #2a2a30; }",
436
+ " .i18nf-prior-title { color: #d4d4d8; }",
437
+ " .i18nf-error { color: #f87171; }",
438
+ " .i18nf-btn-ghost { color: #f4f4f5; }",
439
+ "}",
408
440
  ].join("\n");
409
441
  document.head.appendChild(style);
410
442
  }
@@ -0,0 +1,14 @@
1
+ ar:
2
+ i18n_feedback:
3
+ pill: "اقترح تعديلات"
4
+ pill_active: "وضع الاقتراح — اضغط للخروج (Esc)"
5
+ title: "اقترح تصحيحًا للترجمة"
6
+ current_text: "النص الحالي"
7
+ suggested_text: "النص المقترح"
8
+ comment: "تعليق"
9
+ comment_placeholder: "ملاحظة اختيارية للمطور"
10
+ prior_title: "تم اقتراحه مسبقًا (قيد الانتظار)"
11
+ cancel: "إلغاء"
12
+ save: "إرسال الاقتراح"
13
+ error_blank: "يرجى إدخال اقتراح."
14
+ error_save: "تعذّر حفظ الاقتراح."
@@ -0,0 +1,14 @@
1
+ bn:
2
+ i18n_feedback:
3
+ pill: "সম্পাদনা প্রস্তাব করুন"
4
+ pill_active: "প্রস্তাব চলছে — বের হতে ট্যাপ করুন (Esc)"
5
+ title: "অনুবাদ সংশোধন প্রস্তাব করুন"
6
+ current_text: "বর্তমান লেখা"
7
+ suggested_text: "প্রস্তাবিত লেখা"
8
+ comment: "মন্তব্য"
9
+ comment_placeholder: "ডেভেলপারের জন্য ঐচ্ছিক নোট"
10
+ prior_title: "ইতিমধ্যে প্রস্তাবিত (অপেক্ষমাণ)"
11
+ cancel: "বাতিল"
12
+ save: "প্রস্তাব পাঠান"
13
+ error_blank: "অনুগ্রহ করে একটি প্রস্তাব লিখুন।"
14
+ error_save: "প্রস্তাব সংরক্ষণ করা যায়নি।"
@@ -0,0 +1,14 @@
1
+ de:
2
+ i18n_feedback:
3
+ pill: "Änderungen vorschlagen"
4
+ pill_active: "Vorschlagsmodus — zum Beenden tippen (Esc)"
5
+ title: "Übersetzungskorrektur vorschlagen"
6
+ current_text: "Aktueller Text"
7
+ suggested_text: "Vorgeschlagener Text"
8
+ comment: "Kommentar"
9
+ comment_placeholder: "Optionale Notiz für die Entwicklung"
10
+ prior_title: "Bereits vorgeschlagen (ausstehend)"
11
+ cancel: "Abbrechen"
12
+ save: "Vorschlag senden"
13
+ error_blank: "Bitte gib einen Vorschlag ein."
14
+ error_save: "Der Vorschlag konnte nicht gespeichert werden."
@@ -0,0 +1,14 @@
1
+ en:
2
+ i18n_feedback:
3
+ pill: "Suggest edits"
4
+ pill_active: "Suggesting — tap to exit (Esc)"
5
+ title: "Suggest a translation fix"
6
+ current_text: "Current text"
7
+ suggested_text: "Suggested text"
8
+ comment: "Comment"
9
+ comment_placeholder: "Optional note for the developer"
10
+ prior_title: "Already suggested (pending)"
11
+ cancel: "Cancel"
12
+ save: "Send suggestion"
13
+ error_blank: "Please enter a suggestion."
14
+ error_save: "Could not save the suggestion."
@@ -0,0 +1,14 @@
1
+ es:
2
+ i18n_feedback:
3
+ pill: "Sugerir cambios"
4
+ pill_active: "Sugiriendo — toca para salir (Esc)"
5
+ title: "Sugerir una corrección de traducción"
6
+ current_text: "Texto actual"
7
+ suggested_text: "Texto sugerido"
8
+ comment: "Comentario"
9
+ comment_placeholder: "Nota opcional para el desarrollador"
10
+ prior_title: "Ya sugerido (pendiente)"
11
+ cancel: "Cancelar"
12
+ save: "Enviar sugerencia"
13
+ error_blank: "Introduce una sugerencia."
14
+ error_save: "No se pudo guardar la sugerencia."
@@ -0,0 +1,14 @@
1
+ fr:
2
+ i18n_feedback:
3
+ pill: "Proposer des corrections"
4
+ pill_active: "En cours — appuyez pour quitter (Échap)"
5
+ title: "Proposer une correction de traduction"
6
+ current_text: "Texte actuel"
7
+ suggested_text: "Texte proposé"
8
+ comment: "Commentaire"
9
+ comment_placeholder: "Note facultative pour le développeur"
10
+ prior_title: "Déjà proposé (en attente)"
11
+ cancel: "Annuler"
12
+ save: "Envoyer la suggestion"
13
+ error_blank: "Veuillez saisir une suggestion."
14
+ error_save: "Impossible d'enregistrer la suggestion."
@@ -0,0 +1,14 @@
1
+ hi:
2
+ i18n_feedback:
3
+ pill: "बदलाव सुझाएँ"
4
+ pill_active: "सुझाव मोड — बाहर निकलने के लिए टैप करें (Esc)"
5
+ title: "अनुवाद सुधार सुझाएँ"
6
+ current_text: "वर्तमान टेक्स्ट"
7
+ suggested_text: "सुझाया गया टेक्स्ट"
8
+ comment: "टिप्पणी"
9
+ comment_placeholder: "डेवलपर के लिए वैकल्पिक टिप्पणी"
10
+ prior_title: "पहले से सुझाया गया (लंबित)"
11
+ cancel: "रद्द करें"
12
+ save: "सुझाव भेजें"
13
+ error_blank: "कृपया एक सुझाव दर्ज करें।"
14
+ error_save: "सुझाव सहेजा नहीं जा सका।"
@@ -0,0 +1,14 @@
1
+ id:
2
+ i18n_feedback:
3
+ pill: "Sarankan perubahan"
4
+ pill_active: "Menyarankan — ketuk untuk keluar (Esc)"
5
+ title: "Sarankan perbaikan terjemahan"
6
+ current_text: "Teks saat ini"
7
+ suggested_text: "Teks yang disarankan"
8
+ comment: "Komentar"
9
+ comment_placeholder: "Catatan opsional untuk pengembang"
10
+ prior_title: "Sudah disarankan (menunggu)"
11
+ cancel: "Batal"
12
+ save: "Kirim saran"
13
+ error_blank: "Silakan masukkan saran."
14
+ error_save: "Tidak dapat menyimpan saran."
@@ -0,0 +1,14 @@
1
+ it:
2
+ i18n_feedback:
3
+ pill: "Suggerisci modifiche"
4
+ pill_active: "Suggerimento — tocca per uscire (Esc)"
5
+ title: "Suggerisci una correzione di traduzione"
6
+ current_text: "Testo attuale"
7
+ suggested_text: "Testo suggerito"
8
+ comment: "Commento"
9
+ comment_placeholder: "Nota facoltativa per lo sviluppatore"
10
+ prior_title: "Già suggerito (in sospeso)"
11
+ cancel: "Annulla"
12
+ save: "Invia suggerimento"
13
+ error_blank: "Inserisci un suggerimento."
14
+ error_save: "Impossibile salvare il suggerimento."
@@ -0,0 +1,14 @@
1
+ ja:
2
+ i18n_feedback:
3
+ pill: "修正を提案"
4
+ pill_active: "提案中 — タップで終了(Esc)"
5
+ title: "翻訳の修正を提案"
6
+ current_text: "現在のテキスト"
7
+ suggested_text: "提案するテキスト"
8
+ comment: "コメント"
9
+ comment_placeholder: "開発者への任意のメモ"
10
+ prior_title: "提案済み(保留中)"
11
+ cancel: "キャンセル"
12
+ save: "提案を送信"
13
+ error_blank: "提案を入力してください。"
14
+ error_save: "提案を保存できませんでした。"
@@ -0,0 +1,14 @@
1
+ ko:
2
+ i18n_feedback:
3
+ pill: "수정 제안"
4
+ pill_active: "제안 중 — 종료하려면 탭하세요 (Esc)"
5
+ title: "번역 수정 제안"
6
+ current_text: "현재 텍스트"
7
+ suggested_text: "제안 텍스트"
8
+ comment: "의견"
9
+ comment_placeholder: "개발자를 위한 선택 메모"
10
+ prior_title: "이미 제안됨 (대기 중)"
11
+ cancel: "취소"
12
+ save: "제안 보내기"
13
+ error_blank: "제안을 입력하세요."
14
+ error_save: "제안을 저장할 수 없습니다."
@@ -0,0 +1,14 @@
1
+ nl:
2
+ i18n_feedback:
3
+ pill: "Wijzigingen voorstellen"
4
+ pill_active: "Aan het voorstellen — tik om te sluiten (Esc)"
5
+ title: "Een vertaalcorrectie voorstellen"
6
+ current_text: "Huidige tekst"
7
+ suggested_text: "Voorgestelde tekst"
8
+ comment: "Opmerking"
9
+ comment_placeholder: "Optionele notitie voor de ontwikkelaar"
10
+ prior_title: "Al voorgesteld (in behandeling)"
11
+ cancel: "Annuleren"
12
+ save: "Suggestie versturen"
13
+ error_blank: "Voer een suggestie in."
14
+ error_save: "Kan de suggestie niet opslaan."
@@ -0,0 +1,14 @@
1
+ pl:
2
+ i18n_feedback:
3
+ pill: "Zaproponuj zmiany"
4
+ pill_active: "Tryb sugestii — dotknij, aby wyjść (Esc)"
5
+ title: "Zaproponuj poprawkę tłumaczenia"
6
+ current_text: "Bieżący tekst"
7
+ suggested_text: "Proponowany tekst"
8
+ comment: "Komentarz"
9
+ comment_placeholder: "Opcjonalna notatka dla programisty"
10
+ prior_title: "Już zaproponowano (oczekujące)"
11
+ cancel: "Anuluj"
12
+ save: "Wyślij sugestię"
13
+ error_blank: "Wprowadź sugestię."
14
+ error_save: "Nie udało się zapisać sugestii."
@@ -0,0 +1,14 @@
1
+ pt:
2
+ i18n_feedback:
3
+ pill: "Sugerir edições"
4
+ pill_active: "Sugerindo — toque para sair (Esc)"
5
+ title: "Sugerir uma correção de tradução"
6
+ current_text: "Texto atual"
7
+ suggested_text: "Texto sugerido"
8
+ comment: "Comentário"
9
+ comment_placeholder: "Nota opcional para o desenvolvedor"
10
+ prior_title: "Já sugerido (pendente)"
11
+ cancel: "Cancelar"
12
+ save: "Enviar sugestão"
13
+ error_blank: "Insira uma sugestão."
14
+ error_save: "Não foi possível salvar a sugestão."
@@ -0,0 +1,14 @@
1
+ ru:
2
+ i18n_feedback:
3
+ pill: "Предложить правки"
4
+ pill_active: "Режим предложений — нажмите, чтобы выйти (Esc)"
5
+ title: "Предложить исправление перевода"
6
+ current_text: "Текущий текст"
7
+ suggested_text: "Предлагаемый текст"
8
+ comment: "Комментарий"
9
+ comment_placeholder: "Необязательная заметка для разработчика"
10
+ prior_title: "Уже предложено (в ожидании)"
11
+ cancel: "Отмена"
12
+ save: "Отправить предложение"
13
+ error_blank: "Введите предложение."
14
+ error_save: "Не удалось сохранить предложение."
@@ -0,0 +1,14 @@
1
+ th:
2
+ i18n_feedback:
3
+ pill: "แนะนำการแก้ไข"
4
+ pill_active: "กำลังแนะนำ — แตะเพื่อออก (Esc)"
5
+ title: "แนะนำการแก้ไขคำแปล"
6
+ current_text: "ข้อความปัจจุบัน"
7
+ suggested_text: "ข้อความที่แนะนำ"
8
+ comment: "ความคิดเห็น"
9
+ comment_placeholder: "หมายเหตุเพิ่มเติมสำหรับนักพัฒนา"
10
+ prior_title: "แนะนำไปแล้ว (รอดำเนินการ)"
11
+ cancel: "ยกเลิก"
12
+ save: "ส่งคำแนะนำ"
13
+ error_blank: "โปรดป้อนคำแนะนำ"
14
+ error_save: "ไม่สามารถบันทึกคำแนะนำได้"
@@ -0,0 +1,14 @@
1
+ tr:
2
+ i18n_feedback:
3
+ pill: "Düzenleme öner"
4
+ pill_active: "Öneri modu — çıkmak için dokun (Esc)"
5
+ title: "Çeviri düzeltmesi öner"
6
+ current_text: "Mevcut metin"
7
+ suggested_text: "Önerilen metin"
8
+ comment: "Yorum"
9
+ comment_placeholder: "Geliştirici için isteğe bağlı not"
10
+ prior_title: "Zaten önerildi (beklemede)"
11
+ cancel: "İptal"
12
+ save: "Öneriyi gönder"
13
+ error_blank: "Lütfen bir öneri girin."
14
+ error_save: "Öneri kaydedilemedi."
@@ -0,0 +1,14 @@
1
+ uk:
2
+ i18n_feedback:
3
+ pill: "Запропонувати зміни"
4
+ pill_active: "Режим пропозицій — торкніться, щоб вийти (Esc)"
5
+ title: "Запропонувати виправлення перекладу"
6
+ current_text: "Поточний текст"
7
+ suggested_text: "Запропонований текст"
8
+ comment: "Коментар"
9
+ comment_placeholder: "Необов’язкова примітка для розробника"
10
+ prior_title: "Уже запропоновано (очікує)"
11
+ cancel: "Скасувати"
12
+ save: "Надіслати пропозицію"
13
+ error_blank: "Будь ласка, введіть пропозицію."
14
+ error_save: "Не вдалося зберегти пропозицію."
@@ -0,0 +1,14 @@
1
+ ur:
2
+ i18n_feedback:
3
+ pill: "ترامیم تجویز کریں"
4
+ pill_active: "تجویز موڈ — باہر نکلنے کے لیے تھپتھپائیں (Esc)"
5
+ title: "ترجمے کی اصلاح تجویز کریں"
6
+ current_text: "موجودہ متن"
7
+ suggested_text: "تجویز کردہ متن"
8
+ comment: "تبصرہ"
9
+ comment_placeholder: "ڈویلپر کے لیے اختیاری نوٹ"
10
+ prior_title: "پہلے ہی تجویز کیا جا چکا (زیر التوا)"
11
+ cancel: "منسوخ کریں"
12
+ save: "تجویز بھیجیں"
13
+ error_blank: "براہ کرم ایک تجویز درج کریں۔"
14
+ error_save: "تجویز محفوظ نہیں ہو سکی۔"
@@ -0,0 +1,14 @@
1
+ vi:
2
+ i18n_feedback:
3
+ pill: "Đề xuất chỉnh sửa"
4
+ pill_active: "Đang đề xuất — chạm để thoát (Esc)"
5
+ title: "Đề xuất sửa bản dịch"
6
+ current_text: "Văn bản hiện tại"
7
+ suggested_text: "Văn bản đề xuất"
8
+ comment: "Nhận xét"
9
+ comment_placeholder: "Ghi chú tùy chọn cho nhà phát triển"
10
+ prior_title: "Đã đề xuất (đang chờ)"
11
+ cancel: "Hủy"
12
+ save: "Gửi đề xuất"
13
+ error_blank: "Vui lòng nhập một đề xuất."
14
+ error_save: "Không thể lưu đề xuất."
@@ -0,0 +1,14 @@
1
+ zh-CN:
2
+ i18n_feedback:
3
+ pill: "建议修改"
4
+ pill_active: "建议中 — 点击退出(Esc)"
5
+ title: "建议翻译修正"
6
+ current_text: "当前文本"
7
+ suggested_text: "建议文本"
8
+ comment: "备注"
9
+ comment_placeholder: "给开发者的可选备注"
10
+ prior_title: "已提交建议(待处理)"
11
+ cancel: "取消"
12
+ save: "发送建议"
13
+ error_blank: "请输入建议。"
14
+ error_save: "无法保存建议。"
@@ -38,7 +38,8 @@ module I18nFeedback
38
38
  # from your own link instead (see #toggle_param).
39
39
  attr_accessor :show_pill
40
40
 
41
- # Text on the floating toggle pill.
41
+ # Text on the floating toggle pill. Leave nil to use the localized default
42
+ # ("Suggest edits", translated via the `i18n_feedback.pill` I18n key).
42
43
  attr_accessor :pill_label
43
44
 
44
45
  # The query-string parameter that turns suggest mode on/off, so a host can
@@ -56,7 +57,7 @@ module I18nFeedback
56
57
  @auto_inject = true
57
58
  @mount_path = '/i18n_feedback'
58
59
  @show_pill = true
59
- @pill_label = 'Suggest edits'
60
+ @pill_label = nil
60
61
  @toggle_param = 'i18n_feedback'
61
62
  end
62
63
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nFeedback
4
- VERSION = '0.2.2'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -11,6 +11,10 @@ module I18nFeedback
11
11
  module Widget
12
12
  SOURCE = File.expand_path('../../app/assets/i18n_feedback/widget.js', __dir__)
13
13
 
14
+ # Right-to-left scripts, so the popover renders mirrored for those locales.
15
+ # Matched on the language subtag, so region variants ("ar-EG") count too.
16
+ RTL_LANGUAGES = %w[ar arc ckb dv fa ha he ks ku ps sd ug ur yi].freeze
17
+
14
18
  class << self
15
19
  def javascript
16
20
  @javascript ||= File.read(SOURCE)
@@ -33,7 +37,9 @@ module I18nFeedback
33
37
  active: active ? true : false,
34
38
  showPill: I18nFeedback.config.show_pill ? true : false,
35
39
  pillLabel: I18nFeedback.config.pill_label,
36
- toggleParam: I18nFeedback.config.toggle_param
40
+ toggleParam: I18nFeedback.config.toggle_param,
41
+ labels: labels,
42
+ rtl: rtl?(locale)
37
43
  }
38
44
  # Escape "</" so a value can't close the <script> block early.
39
45
  json = config.to_json.gsub('</', '<\/')
@@ -42,6 +48,37 @@ module I18nFeedback
42
48
  %(<script type="application/json" data-i18n-feedback-config>#{json}</script>) +
43
49
  %(<script data-i18n-feedback-widget#{nonce_attr}>#{javascript}</script>)
44
50
  end
51
+
52
+ private
53
+
54
+ # Every user-facing string in the widget, resolved through Rails I18n so the
55
+ # popover follows the app's current locale. Each lookup carries an English
56
+ # default, so the widget stays fully worded even when the host app hasn't
57
+ # loaded the gem's locale file or is missing a key for the active locale.
58
+ def labels
59
+ {
60
+ pill: t(:pill, 'Suggest edits'),
61
+ pillActive: t(:pill_active, 'Suggesting — tap to exit (Esc)'),
62
+ title: t(:title, 'Suggest a translation fix'),
63
+ currentText: t(:current_text, 'Current text'),
64
+ suggestedText: t(:suggested_text, 'Suggested text'),
65
+ comment: t(:comment, 'Comment'),
66
+ commentPlaceholder: t(:comment_placeholder, 'Optional note for the developer'),
67
+ priorTitle: t(:prior_title, 'Already suggested (pending)'),
68
+ cancel: t(:cancel, 'Cancel'),
69
+ save: t(:save, 'Send suggestion'),
70
+ errorBlank: t(:error_blank, 'Please enter a suggestion.'),
71
+ errorSave: t(:error_save, 'Could not save the suggestion.')
72
+ }
73
+ end
74
+
75
+ def t(key, default)
76
+ I18n.t(key, scope: :i18n_feedback, default: default)
77
+ end
78
+
79
+ def rtl?(locale)
80
+ RTL_LANGUAGES.include?(locale.to_s.downcase.split(/[-_]/).first)
81
+ end
45
82
  end
46
83
  end
47
84
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n_feedback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -44,6 +44,27 @@ files:
44
44
  - app/helpers/i18n_feedback/tag_helper.rb
45
45
  - app/models/i18n_feedback/application_record.rb
46
46
  - app/models/i18n_feedback/suggestion.rb
47
+ - config/locales/i18n_feedback.ar.yml
48
+ - config/locales/i18n_feedback.bn.yml
49
+ - config/locales/i18n_feedback.de.yml
50
+ - config/locales/i18n_feedback.en.yml
51
+ - config/locales/i18n_feedback.es.yml
52
+ - config/locales/i18n_feedback.fr.yml
53
+ - config/locales/i18n_feedback.hi.yml
54
+ - config/locales/i18n_feedback.id.yml
55
+ - config/locales/i18n_feedback.it.yml
56
+ - config/locales/i18n_feedback.ja.yml
57
+ - config/locales/i18n_feedback.ko.yml
58
+ - config/locales/i18n_feedback.nl.yml
59
+ - config/locales/i18n_feedback.pl.yml
60
+ - config/locales/i18n_feedback.pt.yml
61
+ - config/locales/i18n_feedback.ru.yml
62
+ - config/locales/i18n_feedback.th.yml
63
+ - config/locales/i18n_feedback.tr.yml
64
+ - config/locales/i18n_feedback.uk.yml
65
+ - config/locales/i18n_feedback.ur.yml
66
+ - config/locales/i18n_feedback.vi.yml
67
+ - config/locales/i18n_feedback.zh-CN.yml
47
68
  - config/routes.rb
48
69
  - lib/generators/i18n_feedback/install/install_generator.rb
49
70
  - lib/generators/i18n_feedback/install/templates/create_i18n_feedback_suggestions.rb.tt