Сложное форматирование строки с учетом словоформ, мультиязычности и вставки единиц измерений

WBS

Новичок
Речь идет об аналоге стандартной функции vsprintf(), только с немного иным функционалом.


Исходные данные

Шаблон:
"{duration}<br>{animals}: {cats_pcnt} кошек и {dogs_pcnt} собак"

Массив:
Код:
$arr = array(
	"duration" => 190,
	"animals" => 21,
	"cats_pcnt" => 71.428571,
	"dogs_pcnt" => 28.571429,
);

Какие-то данные, обеспечивающие мультиязычность (с пока неясным форматом):
Код:
$lang = array(
	"ru" => ...,
	"en" => ...,
	...
);

Требуется вернуть форматированную строку.

1. С учетом вставки единиц измерения.
duration (задано в секундах) => ".. часов .. минут"
animals => ".. животных"
cats_pcnt => "..%"
dogs_pcnt => "..%"

2. С правильным окончанием слов (1 час, 2 часа, 5 часов).

3. На указанном языке.


Ожидаемый результат работы функции.

Для "ru"
"3 часа 10 минут<br>21 животное: 71.4% кошек и 28.6% собак"

Для "en"
"3 hours 10 minutes<br>21 animal: 71.4% cats and 28.6% dogs"


На данный момент имеется:

1. Функция
num_ending ($number, $ending_arr)
Возвращает слово в правильной форме для количества $number.
num_ending(15, array("день", "дня", "дней")) => "дней"

2. Функция
sec_to_dhms ($sec)
Преобразует секунды $sec в массив вида
array("d" => {дни}, "h" => {часы}, "m" => {минуты}, "s" => {секунды})
sec_to_dhms(190) => array("h" => 3, "m" => 10)


Вопросы:

1. Как грамотно организовать хранение исходных данных?
В идеале шаблон нужно полностью "отделить" от языка.

2. Как обеспечить взаимодействие между всем этим добром?
Т.е. пока мне не очень понятна общая структура функции и механизм передачи различных переменных (в т.ч. списков слов для разных словоформ и языков).


Некоторые мои мысли по реализации.

На данный момент я прямо в шаблоне прописываю единицы измерения (один символ после фигурных скобок). Примерно так:
"{duration}t<br>{animals}a: {cats_pcnt}% кошек и {dogs_pcnt}% собак"

В функции я разбираю шаблон используя регулярные выражения
Код:
if ($count = preg_match_all("/\{([a-z_]+)\}(.{1})/Usi", $pattern, $matches)) {
	for ($i=0; $i<$count; $i++) {
		...
	}
}
В цикле я по-разному обрабатываю данные в зависимости от единиц измерения.
- если "t" (время) - вызываю sec_to_dhms() и форматирую как время;
- если "a" (животное) - вызываю num_ending() и отформатирую как ".. животных" (в правильной словоформе);
- если "%" (процент) - округляю до десятых и добавляю знак процента.

Далее я заполняю шаблон через str_replace().

Поддержка мультиязычности у меня пока не реализована.


В общем, с удовольствием послушаю чужие мысли на этот счет.
 

Redjik

Джедай-мастер
Сам у кого то упер - дарю

PHP:
	public function productCase(){
		$n = $this->countProducts();
		$forms=array('товар', 'товара', 'товаров');		
		return $n%10==1&&$n%100!=11?$forms[0]:($n%10>=2&&$n%10<=4&&($n%100<10||$n%100>=20)?$forms[1]:$forms[2]);
		
	}
 

radioheaded

PHP нуб
Стандартная 1-2-5 задача. Вам нужно будет прописывать у каждого слова все 3 формы в любом удобном вам формате (либо в БД, либо прямо в шаблоне каждый раз). Если у вас есть весьма ограниченный набор лексем, то можно заморочиться с падежами. Вот, например, как делает FB:
{name1} прокомментировал свою {=photo # Падеж:Винительный} с Вами
. Или же просто-напросто написать лексический анализатор, который сам будет находить нужную форму слова и подставлять ее.

Сначала сделайте, чтобы работало хоть как-то. Затем улучшайте, если есть необходимость.
 

WBS

Новичок
Сам у кого то упер - дарю
Это, как я уже писал, у меня уже есть. И я, в свою очередь, дарю Вам свою реализацию :)
PHP:
function num_ending ($number, $ending_arr) {
// Функция возвращает слово в правильной форме для количества $number.
// $ending_arr - список слов для количества 1, 2 и 5.
// Примеры:
// num_ending(243, array("кролик", "кролика", "кроликов")) => "кролика"
// num_ending(15, array("день", "дня", "дней")) => "дней"
	$number %= 100;
	if ($number>=11 && $number<=19)
		$ending = $ending_arr[2];
	else {
		$number %= 10;
		switch ($number) {
			case (1): $ending = $ending_arr[0]; break;
			case (2): case (3): case (4): $ending = $ending_arr[1]; break;
			default: $ending = $ending_arr[2];
		}
	}
	return $ending;
}
Мне бы хотелось услышать советы по поводу реализации того, чего у меня нет. Например, по поводу реализации мультиязычности.
 

WBS

Новичок
Сначала сделайте, чтобы работало хоть как-то. Затем улучшайте, если есть необходимость.
Хоть как-то у меня уже давно работает (и я описал в посте как). Я обратился на форум как раз чтобы получить советы по улучшению.

Например, по той же мультиязычности...
- как составить шаблон в общем виде без использования какого-либо языка?
- как хранить информацию о языках, какие слова хранить, в каком формате?
- как передавать данные о языке в функцию, что именно передавать?

Или общие вопросы, например, нормально ли заполнять шаблон используя функцию str_replace?

Уверен, что на форуме есть люди, которые работали с шаблонами (не обязательно с шаблонами строк, можно и с шаблонами целых страниц). Хотелось бы перенять их опыт.
 

radioheaded

PHP нуб
- как составить шаблон в общем виде без использования какого-либо языка?
Никак. Какой-то язык должен быть взят за оригинал. С него будут переводы на другие языки. Вы можете, конечно, извратиться и подставлять в шаблоны не лексемы, а некие идентификаторы, а сами лексемы вводить в бд как-то иначе, просто не понятно, зачем.

- как хранить информацию о языках, какие слова хранить, в каком формате?
Да как вам удобно. Чаще всего в шаблоне лексемы размечаются любым подходящим способом, затем шаблоны обрабатываются, лексемы выдираются и отправляются на перевод. В некоем интерфейсе переводчики видят лексемы и переводят их на нужный язык. Затем вы генерируете шаблоны, подставляя туда любым способом переведенные лексемы.

- как передавать данные о языке в функцию, что именно передавать?
В какую еще функцию? Пользователь переключает язык и вы просто начинаете показывать ему другие, заранее подготовленные шаблоны. Раз уж вы взялись за преждевременную оптимизацию, то вы же не собираетесь их в рантайме собирать?
 
  • Like
Реакции: WBS

Beavis

Banned
я в шаблонах смарти делал так:

PHP:
{$n} {plural n=$n f1="час" f2="часа" f5="часов"}
 

alekciy

Новичок
Мне бы хотелось услышать советы по поводу реализации того, чего у меня нет. Например, по поводу реализации мультиязычности.
А чего тут думать? gettext проект с многолетней историей работающий на многих платформах. А свои эти велосипеды нужно безжалостно выпиливать. По сабжу: gettext plural form.
 
  • Like
Реакции: WBS
Сверху