fix(i18n): migrate powered_by to named placeholders across all locales (#4661, #4727)

Replace hardcoded string concatenation of the app name and version in the
document <title> with named-placeholder interpolation, consistent with the
existing Common.migration_needed pattern.

- header.php passes ['OSPOS', application_version] as the lang() array
  argument instead of concatenating values around the translation
- All 46 Common.php locale files convert powered_by to a named-placeholder
  template, preserving current rendered output while allowing each locale
  to reorder the app name and version as needed
- hy, ml, nb, and ur leave powered_by empty so MY_Language::getLine()
  falls back to English rather than rendering bare placeholder strings
- de-CH powered_by translated to German
- Codify named-placeholder + ICU MessageFormat as the localization standard
  in the agents documentation
This commit is contained in:
jekkos authored and GitHub committed 2026-10-28 14:10:27 +04:00
1 parent 600417bfdf
commit 8a8736ad0d
44 files changed
+45 -43

No files matched your search

+2
View File
@@ -98,6 +98,8 @@ This document provides guidance for AI agents working on the Open Source Point o
- Never copy English text from a neighboring key as a value for a non-English language file, even if that neighboring key is already untranslated — evaluate each key independently
- Only `app/Language/en/` and `app/Language/en-GB/` should contain English strings
- When translating a string containing placeholders (e.g. `{filePath}`, `{reason}`) or literal filenames/keys (e.g. `throttle.key`, `.env`), keep that placeholder or filename text unchanged and untranslated — move it to whatever position is grammatically correct in the translated sentence
- Interpolate user-facing strings with `lang()` and **named** placeholders (e.g. `{company}`, `{count}`) rather than string concatenation or positional placeholders (`{0}`, `{1}`) — this lets a translation reorder the placeholder to fit grammar, and named placeholders are self-documenting for translators (see `app/Helpers/security_helper.php:441`). CodeIgniter 4.7+ formats these via ICU `MessageFormatter` (the `intl` extension is a hard dependency), so ICU MessageFormat syntax is also available where a string needs it, e.g. `lang('...', ['count' => $n])` with `'{count, plural, =0 {None} one {# item} other {# items}}'`
- Because placeholders are formatted by ICU `MessageFormatter`, a **placeholder-bearing** translation must be valid ICU MessageFormat: double any literal braces (`{{`/`}}`) and any apostrophe (`''`) to render them literally, or `MessageFormatter` fails and CodeIgniter appends a formatting warning to the output. Placeholder-free strings are never passed to ICU, so they are unaffected
- Plugin language files (`app/Plugins/*/Language/`) follow the same localization rules as `app/Language/`
- Use `'` to encapsulate key and string values. If the value contains `'` then it should be escaped as `\'`
- Align the `=>` of a newly inserted key with the `=>` column already used by the rest of the file, if that file pads keys to a fixed column (not all do — some files have no padding at all). If the new key is longer than the widest existing key and would push the alignment column further right, reformat the whole file to the new wider column rather than leaving only the new line at a different width
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'رقم التليفون مطلوب',
'please_visit_my' => 'برجاء زيارة',
'position' => '',
'powered_by' => 'مدعوم بواسطة',
'powered_by' => 'مدعوم بواسطة {app} {version}',
'price' => 'السعر',
'print' => 'طباعة',
'remove' => 'إزالة',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'رقم التليفون مطلوب',
'please_visit_my' => 'برجاء زيارة',
'position' => '',
'powered_by' => 'مدعوم بواسطة',
'powered_by' => 'مدعوم بواسطة {app} {version}',
'price' => 'السعر',
'print' => 'طباعة',
'remove' => 'إزالة',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Telefon nömrəsi tələb olunan sahədir.',
'please_visit_my' => 'Zəhmət olmasa ziyarət edin',
'position' => 'Vəzifə',
'powered_by' => 'Dəstəkləyən',
'powered_by' => 'Yaradıcı {app} {version}',
'price' => 'Qiymət',
'print' => 'Çap Et',
'remove' => 'Sil',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Моля, посетете',
'position' => '',
'powered_by' => 'С подкрепата на',
'powered_by' => 'С подкрепата на {app} {version}',
'price' => 'Цена',
'print' => 'Принтиране',
'remove' => 'Премахване',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Broj telefona je obavezan',
'please_visit_my' => 'Posjetite',
'position' => '',
'powered_by' => 'Podržano od',
'powered_by' => 'Podržano od {app} {version}',
'price' => 'Cijena',
'print' => 'Štampanje',
'remove' => 'Ukloni',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'تکایە سەردانی',
'position' => '',
'powered_by' => 'پاڵپشتی دەکرێت لەلایەن',
'powered_by' => 'پاڵپشتی دەکرێت لەلایەن {app} {version}',
'price' => 'نرخ',
'print' => 'چاپکردن',
'remove' => 'لابردن',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Navštivte prosím',
'position' => '',
'powered_by' => 'Používá',
'powered_by' => 'Používá {app} {version}',
'price' => 'Cena',
'print' => 'Tisk',
'remove' => 'Vyjmout',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Besøg venligst',
'position' => '',
'powered_by' => 'Drevet af',
'powered_by' => 'Drevet af {app} {version}',
'price' => 'Pris',
'print' => 'Print ud',
'remove' => 'Fjern',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Telefon ist erforderlich',
'please_visit_my' => 'Bitte beuschen Sie ',
'position' => '',
'powered_by' => 'Powered by',
'powered_by' => 'Unterstützt von {app} {version}',
'price' => 'Preis',
'print' => 'Drucken',
'remove' => 'Annulieren',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Telefon ist erforderlich',
'please_visit_my' => 'Bitte beuschen Sie',
'position' => 'Position',
'powered_by' => 'Unterstützt von',
'powered_by' => 'Unterstützt von {app} {version}',
'price' => 'Preis',
'print' => 'Drucken',
'remove' => 'Annulieren',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Παρακαλώ επισκεφτείτε το',
'position' => '',
'powered_by' => 'Τροφοδοτείται από',
'powered_by' => 'Τροφοδοτείται από {app} {version}',
'price' => 'Τιμή',
'print' => 'Εκτύπωση',
'remove' => 'Αφαίρεση',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Phone Number is a required field',
'please_visit_my' => 'Please visit the',
'position' => '',
'powered_by' => 'Powered by',
'powered_by' => 'Powered by {app} {version}',
'price' => 'Price',
'print' => 'Print',
'remove' => 'Remove',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Please visit the',
'position' => '',
'powered_by' => 'Powered by',
'powered_by' => 'Powered by {app} {version}',
'price' => 'Price',
'print' => 'Print',
'remove' => 'Remove',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'El numoero de telefono es requerido',
'please_visit_my' => 'Por favor, visita mi',
'position' => 'Posición',
'powered_by' => 'Potenciado por',
'powered_by' => 'Potenciado por {app} {version}',
'price' => 'Precio',
'print' => 'Imprimir',
'remove' => 'Eliminar',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Por favor visite',
'position' => '',
'powered_by' => 'Ejecutando sobre',
'powered_by' => 'Ejecutando sobre {app} {version}',
'price' => 'Precio',
'print' => 'Imprimir',
'remove' => 'Elliminar',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'لطفا سر بزنید',
'position' => '',
'powered_by' => 'طراحی شده توسط',
'powered_by' => 'طراحی شده توسط {app} {version}',
'price' => 'قیمت',
'print' => 'چاپ',
'remove' => 'برداشتن',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Le numéro de téléphone est requis.',
'please_visit_my' => 'SVP visitez le',
'position' => 'Position',
'powered_by' => 'Propulsé par',
'powered_by' => 'Propulsé par {app} {version}',
'price' => 'Prix',
'print' => 'Imprimer',
'remove' => 'Enlever',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'בקר בכתובת',
'position' => '',
'powered_by' => 'מונע ע\'י',
'powered_by' => 'מונע ע\'י {app} {version}',
'price' => 'מחיר',
'print' => 'הדפס',
'remove' => 'הסר',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Broj telefona je obavezan',
'please_visit_my' => 'Molim posjetite',
'position' => '',
'powered_by' => 'Pokreće',
'powered_by' => 'Pokreće {app} {version}',
'price' => 'Ár',
'print' => 'Štampati',
'remove' => 'Ukloni',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Telefonszám kötelező mező',
'please_visit_my' => 'Kérem látogassa meg a ',
'position' => '',
'powered_by' => 'Powered by',
'powered_by' => 'Powered by {app} {version}',
'price' => 'Ár',
'print' => 'Nyomtat',
'remove' => 'Eltávolít',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Nomer Telepon Wajib Diisi',
'please_visit_my' => 'Silahkan kunjungi',
'position' => '',
'powered_by' => 'Diberdayakan oleh',
'powered_by' => 'Diberdayakan oleh {app} {version}',
'price' => 'Harga',
'print' => 'Cetak',
'remove' => 'Hapus',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Il numero di telefono è richiesto',
'please_visit_my' => 'Visitare il',
'position' => 'Posizione',
'powered_by' => 'Sviluppato da',
'powered_by' => 'Sviluppato da {app} {version}',
'price' => 'Prezzo',
'print' => 'Stampa',
'remove' => 'Rimuovi',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Please visit the',
'position' => '',
'powered_by' => 'Powered by',
'powered_by' => 'Powered by {app} {version}',
'price' => 'ფასი',
'print' => 'დაბეჭდვა',
'remove' => 'წაშლა',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'សូមកាន់',
'position' => '',
'powered_by' => 'ដំណើរការដោយ',
'powered_by' => 'ដំណើរការដោយ {app} {version}',
'price' => 'តម្លៃ',
'print' => 'បោះពុម្ព',
'remove' => 'យកចេញ',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'ກະລູນາເຂົ້າບ່ອນນີ້້',
'position' => '',
'powered_by' => 'ຂັບເຄື່ອນໂດຍ',
'powered_by' => 'ຂັບເຄື່ອນໂດຍ {app} {version}',
'price' => 'ລາຄາ',
'print' => 'ສັ່ງພິມ',
'remove' => 'ລຶບ',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Bezoek mijn',
'position' => '',
'powered_by' => 'Mogelijk gemaakt door',
'powered_by' => 'Mogelijk gemaakt door {app} {version}',
'price' => 'Prijs',
'print' => 'Print',
'remove' => 'Verwijder',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Telefoonnummer is vereist',
'please_visit_my' => 'Bezoek de',
'position' => 'Positie',
'powered_by' => 'Mogelijk gemaakt door',
'powered_by' => 'Mogelijk gemaakt door {app} {version}',
'price' => 'Prijs',
'print' => 'Afdrukken',
'remove' => 'Verwijderen',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Proszę odwiedzić',
'position' => '',
'powered_by' => 'Wspierane przez',
'powered_by' => 'Wspierane przez {app} {version}',
'price' => 'Cena',
'print' => 'Wydrukuj',
'remove' => 'Usuń',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Número do telefone é requerido',
'please_visit_my' => 'Para saber mais sobre esta aplicação visite o website do projeto |',
'position' => 'Posição',
'powered_by' => 'Desenvolvido por',
'powered_by' => 'Desenvolvido por {app} {version}',
'price' => 'Preço',
'print' => 'Imprimir',
'remove' => 'Remover',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Vă rugăm să vizitați',
'position' => '',
'powered_by' => 'Sustinut de',
'powered_by' => 'Sustinut de {app} {version}',
'price' => 'Preț',
'print' => 'Printează',
'remove' => 'Elimină',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Номер телефона - обязательное поле для заполнения.',
'please_visit_my' => 'Посетите',
'position' => 'Должность',
'powered_by' => 'ПО:',
'powered_by' => 'ПО: {app} {version}',
'price' => 'Цена',
'print' => 'Печать',
'remove' => 'Удалить',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Vänligen besök',
'position' => '',
'powered_by' => 'Drivs av',
'powered_by' => 'Drivs av {app} {version}',
'price' => 'Pris',
'print' => 'Skriv ut',
'remove' => 'Radera',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Tafadhali tembelea',
'position' => '',
'powered_by' => 'Imewezeshwa na',
'powered_by' => 'Imewezeshwa na {app} {version}',
'price' => 'Bei',
'print' => 'Chapisha',
'remove' => 'Ondoa',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Tafadhali tembelea',
'position' => '',
'powered_by' => 'Imewezeshwa na',
'powered_by' => 'Imewezeshwa na {app} {version}',
'price' => 'Bei',
'print' => 'Chapisha',
'remove' => 'Ondoa',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'தயவுசெய்து பார்வையிடவும்',
'position' => '',
'powered_by' => 'மூலம் இயக்கப்படுகிறது',
'powered_by' => 'மூலம் இயக்கப்படுகிறது {app} {version}',
'price' => 'விலை',
'print' => 'அச்சிடுக',
'remove' => 'அகற்று',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'ต้องกรอกเบอร์โทร',
'please_visit_my' => 'เยี่ยมชมที่',
'position' => '',
'powered_by' => 'จัดทำโดย',
'powered_by' => 'จัดทำโดย {app} {version}',
'price' => 'ราคา',
'print' => 'พิมพ์',
'remove' => 'ลบ',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Please visit the',
'position' => '',
'powered_by' => 'Powered by',
'powered_by' => 'Powered by {app} {version}',
'price' => 'Price',
'print' => 'Print',
'remove' => 'Remove',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Şurayı ziyaret edin:',
'position' => '',
'powered_by' => 'Destekleyen',
'powered_by' => 'Destekleyen {app} {version}',
'price' => 'Fiyat',
'print' => 'Yazdır',
'remove' => 'Kaldır',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => 'Номер телефону - обов\'язкове поле.',
'please_visit_my' => 'Будь ласка, перейдіть',
'position' => '',
'powered_by' => 'Працює на',
'powered_by' => 'Працює на {app} {version}',
'price' => 'Ціна',
'print' => 'Друк',
'remove' => 'Видалити',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => 'Ghé thăm',
'position' => '',
'powered_by' => 'Cung cấp bởi',
'powered_by' => 'Cung cấp bởi {app} {version}',
'price' => 'Giá',
'print' => 'In',
'remove' => 'Xóa bỏ',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => '请造访',
'position' => '',
'powered_by' => '供应',
'powered_by' => '供应 {app} {version}',
'price' => '价格',
'print' => '打印',
'remove' => '移除',
+1 -1
View File
@@ -63,7 +63,7 @@ return [
'phone_number_required' => '',
'please_visit_my' => '請造訪',
'position' => '',
'powered_by' => '提供者',
'powered_by' => '提供者 {app} {version}',
'price' => '價值',
'print' => '列印',
'remove' => '移除',
+1 -1
View File
@@ -17,7 +17,7 @@ $request = Services::request();
<head>
<meta charset="utf-8">
<base href="<?= base_url() ?>">
<title><?= esc($config['company']) . ' | ' . lang('Common.powered_by') . ' OSPOS ' . esc(config('App')->application_version) ?></title>
<title><?= esc($config['company']) . ' | ' . lang('Common.powered_by', ['app' => 'OSPOS', 'version' => esc(config('App')->application_version)]) ?></title>
<meta name="robots" content="noindex, nofollow">
<link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico">
<?php $theme = (empty($config['theme']) ? 'flatly' : esc($config['theme'])); ?>