// Country pricing + local currency display
function pooliva_language_catalog(): array {
// ISO-style language catalog for global auto-language. Labels are English admin labels; UI strings can be AI-translated and cached.
return [
'af'=>'Afrikaans','sq'=>'Albanian','am'=>'Amharic','ar'=>'Arabic','hy'=>'Armenian','az'=>'Azerbaijani','eu'=>'Basque','be'=>'Belarusian','bn'=>'Bengali','bs'=>'Bosnian','bg'=>'Bulgarian','my'=>'Burmese','ca'=>'Catalan','ceb'=>'Cebuano','zh'=>'Chinese','zh-CN'=>'Chinese Simplified','zh-TW'=>'Chinese Traditional','co'=>'Corsican','hr'=>'Croatian','cs'=>'Czech','da'=>'Danish','nl'=>'Dutch','en'=>'English','eo'=>'Esperanto','et'=>'Estonian','fi'=>'Finnish','fr'=>'French','fy'=>'Frisian','gl'=>'Galician','ka'=>'Georgian','de'=>'German','el'=>'Greek','gu'=>'Gujarati','ht'=>'Haitian Creole','ha'=>'Hausa','haw'=>'Hawaiian','he'=>'Hebrew','hi'=>'Hindi','hmn'=>'Hmong','hu'=>'Hungarian','is'=>'Icelandic','ig'=>'Igbo','id'=>'Indonesian','ga'=>'Irish','it'=>'Italian','ja'=>'Japanese','jv'=>'Javanese','kn'=>'Kannada','kk'=>'Kazakh','km'=>'Khmer','rw'=>'Kinyarwanda','ko'=>'Korean','ku'=>'Kurdish','ky'=>'Kyrgyz','lo'=>'Lao','la'=>'Latin','lv'=>'Latvian','lt'=>'Lithuanian','lb'=>'Luxembourgish','mk'=>'Macedonian','mg'=>'Malagasy','ms'=>'Malay','ml'=>'Malayalam','mt'=>'Maltese','mi'=>'Maori','mr'=>'Marathi','mn'=>'Mongolian','ne'=>'Nepali','no'=>'Norwegian','ny'=>'Nyanja','or'=>'Odia','ps'=>'Pashto','fa'=>'Persian','pl'=>'Polish','pt'=>'Portuguese','pa'=>'Punjabi','ro'=>'Romanian','ru'=>'Russian','sm'=>'Samoan','gd'=>'Scots Gaelic','sr'=>'Serbian','st'=>'Sesotho','sn'=>'Shona','sd'=>'Sindhi','si'=>'Sinhala','sk'=>'Slovak','sl'=>'Slovenian','so'=>'Somali','es'=>'Spanish','su'=>'Sundanese','sw'=>'Swahili','sv'=>'Swedish','tl'=>'Tagalog','tg'=>'Tajik','ta'=>'Tamil','tt'=>'Tatar','te'=>'Telugu','th'=>'Thai','tr'=>'Turkish','tk'=>'Turkmen','uk'=>'Ukrainian','ur'=>'Urdu','ug'=>'Uyghur','uz'=>'Uzbek','vi'=>'Vietnamese','cy'=>'Welsh','xh'=>'Xhosa','yi'=>'Yiddish','yo'=>'Yoruba','zu'=>'Zulu'
];
}
function pooliva_language_is_rtl(string $lang): bool {
$base = strtolower(explode('-', $lang)[0]);
return in_array($base, ['ar','fa','he','ur','ps','sd','ug','ku'], true);
}
function pooliva_country_language_map(): array {
// Covers virtually all ISO-3166 countries with a practical default language for auto-language.
return [
'US'=>'en','CA'=>'en','GB'=>'en','AU'=>'en','NZ'=>'en','IE'=>'en','ZA'=>'en','NG'=>'en','KE'=>'sw','TZ'=>'sw','UG'=>'en','GH'=>'en','PH'=>'en','IN'=>'hi','PK'=>'ur','BD'=>'bn','LK'=>'si','NP'=>'ne',
'SA'=>'ar','AE'=>'ar','QA'=>'ar','KW'=>'ar','BH'=>'ar','OM'=>'ar','YE'=>'ar','JO'=>'ar','LB'=>'ar','SY'=>'ar','IQ'=>'ar','EG'=>'ar','SD'=>'ar','LY'=>'ar','TN'=>'ar','DZ'=>'ar','MA'=>'ar','PS'=>'ar',
'BR'=>'pt','PT'=>'pt','AO'=>'pt','MZ'=>'pt','CV'=>'pt','GW'=>'pt','ST'=>'pt','TL'=>'pt',
'ES'=>'es','MX'=>'es','AR'=>'es','CO'=>'es','PE'=>'es','CL'=>'es','VE'=>'es','EC'=>'es','BO'=>'es','PY'=>'es','UY'=>'es','CR'=>'es','PA'=>'es','GT'=>'es','HN'=>'es','SV'=>'es','NI'=>'es','DO'=>'es','CU'=>'es','PR'=>'es',
'FR'=>'fr','BE'=>'fr','CH'=>'de','LU'=>'fr','MC'=>'fr','CI'=>'fr','SN'=>'fr','CM'=>'fr','ML'=>'fr','NE'=>'fr','BF'=>'fr','BJ'=>'fr','TG'=>'fr','GA'=>'fr','CG'=>'fr','CD'=>'fr','RW'=>'rw','BI'=>'fr','DJ'=>'fr','KM'=>'fr','MG'=>'mg',
'DE'=>'de','AT'=>'de','NL'=>'nl','SE'=>'sv','NO'=>'no','DK'=>'da','FI'=>'fi','IS'=>'is','IT'=>'it','SM'=>'it','VA'=>'it','GR'=>'el','CY'=>'el','MT'=>'mt','PL'=>'pl','CZ'=>'cs','SK'=>'sk','HU'=>'hu','RO'=>'ro','MD'=>'ro','BG'=>'bg','HR'=>'hr','SI'=>'sl','RS'=>'sr','BA'=>'bs','ME'=>'sr','MK'=>'mk','AL'=>'sq','XK'=>'sq','TR'=>'tr','RU'=>'ru','UA'=>'uk','BY'=>'be','EE'=>'et','LV'=>'lv','LT'=>'lt','GE'=>'ka','AM'=>'hy','AZ'=>'az','KZ'=>'kk','KG'=>'ky','TJ'=>'tg','TM'=>'tk','UZ'=>'uz',
'CN'=>'zh-CN','TW'=>'zh-TW','HK'=>'zh-TW','MO'=>'zh-TW','JP'=>'ja','KR'=>'ko','KP'=>'ko','VN'=>'vi','TH'=>'th','ID'=>'id','MY'=>'ms','SG'=>'en','KH'=>'km','LA'=>'lo','MM'=>'my','MN'=>'mn','BN'=>'ms',
'IR'=>'fa','AF'=>'fa','IL'=>'he','ET'=>'am','SO'=>'so','ER'=>'ti','ZW'=>'sn','ZM'=>'en','MW'=>'ny','MZ'=>'pt','BW'=>'en','NA'=>'en','LS'=>'st','SZ'=>'en','MG'=>'mg','MU'=>'en','SC'=>'en',
'JM'=>'en','HT'=>'ht','BZ'=>'en','GY'=>'en','SR'=>'nl','TT'=>'en','BB'=>'en','BS'=>'en','AG'=>'en','DM'=>'en','GD'=>'en','KN'=>'en','LC'=>'en','VC'=>'en','AW'=>'nl','CW'=>'nl'
];
}
function pooliva_country_currency_map(): array {
// Currency/language defaults for the most important ad/payment countries. Missing countries safely fall back to USD + country language.
$m = [
'US'=>['USD','$',1.00,'en'], 'CA'=>['CAD','C$',1.36,'en'], 'GB'=>['GBP','£',0.79,'en'], 'AU'=>['AUD','A$',1.52,'en'], 'NZ'=>['NZD','NZ$',1.64,'en'],
'SA'=>['SAR','ر.س',3.75,'ar'], 'AE'=>['AED','د.إ',3.67,'ar'], 'QA'=>['QAR','ر.ق',3.64,'ar'], 'KW'=>['KWD','د.ك',0.31,'ar'], 'BH'=>['BHD','BD',0.38,'ar'], 'OM'=>['OMR','OMR',0.38,'ar'], 'EG'=>['EGP','E£',48.00,'ar'], 'MA'=>['MAD','DH',10.00,'ar'],
'BR'=>['BRL','R$',5.15,'pt'], 'MX'=>['MXN','$',17.00,'es'], 'AR'=>['ARS','$',900.00,'es'], 'CO'=>['COP','$',3900.00,'es'], 'CL'=>['CLP','$',920.00,'es'], 'PE'=>['PEN','S/',3.75,'es'],
'TR'=>['TRY','₺',32.50,'tr'], 'IN'=>['INR','₹',83.00,'hi'], 'ID'=>['IDR','Rp',16000,'id'], 'PH'=>['PHP','₱',56.00,'en'], 'TH'=>['THB','฿',36.00,'th'], 'MY'=>['MYR','RM',4.70,'ms'], 'SG'=>['SGD','S$',1.35,'en'],
'JP'=>['JPY','¥',155.00,'ja'], 'KR'=>['KRW','₩',1360.00,'ko'], 'CN'=>['CNY','¥',7.20,'zh-CN'], 'TW'=>['TWD','NT$',32.00,'zh-TW'], 'HK'=>['HKD','HK$',7.80,'zh-TW'],
'FR'=>['EUR','€',0.92,'fr'], 'DE'=>['EUR','€',0.92,'de'], 'ES'=>['EUR','€',0.92,'es'], 'IT'=>['EUR','€',0.92,'it'], 'NL'=>['EUR','€',0.92,'nl'], 'BE'=>['EUR','€',0.92,'fr'], 'AT'=>['EUR','€',0.92,'de'], 'IE'=>['EUR','€',0.92,'en'], 'PT'=>['EUR','€',0.92,'pt'], 'GR'=>['EUR','€',0.92,'el'], 'FI'=>['EUR','€',0.92,'fi'],
'SE'=>['SEK','kr',10.60,'sv'], 'NO'=>['NOK','kr',10.70,'no'], 'DK'=>['DKK','kr',6.90,'da'], 'PL'=>['PLN','zł',4.00,'pl'], 'CZ'=>['CZK','Kč',23.00,'cs'], 'RO'=>['RON','lei',4.60,'ro'], 'HU'=>['HUF','Ft',360.00,'hu'], 'BG'=>['BGN','лв',1.80,'bg'],
'RU'=>['RUB','₽',92.00,'ru'], 'UA'=>['UAH','₴',39.00,'uk'], 'ZA'=>['ZAR','R',18.50,'en'], 'NG'=>['NGN','₦',1500.00,'en'], 'KE'=>['KES','KSh',130.00,'sw']
];
$langMap = pooliva_country_language_map();
foreach ($langMap as $cc=>$lang) if (!isset($m[$cc])) $m[$cc]=['USD','$',1.00,$lang];
return $m;
}
function pooliva_geo_profile(?string $cc=null): array {
$cc = strtoupper($cc ?: country_code());
$map = pooliva_country_currency_map();
$d = $map[$cc] ?? ['USD','$',1.00,(pooliva_country_language_map()[$cc] ?? 'en')];
return ['country_code'=>$cc,'currency_code'=>$d[0],'currency_symbol'=>$d[1],'fx_rate'=>(float)$d[2],'language'=>$d[3]];
}
function pooliva_country_price_row(string $cc): ?array {
global $pdo;
try { $st=$pdo->prepare('SELECT * FROM country_pricing WHERE country_code=? AND is_active=1 LIMIT 1'); $st->execute([strtoupper($cc)]); $r=$st->fetch(); return $r ?: null; }
catch(Throwable $e){ return null; }
}
function pooliva_pricing_for_country(?string $cc=null): array {
$geo = pooliva_geo_profile($cc);
$row = pooliva_country_price_row($geo['country_code']);
$credits = (float)($row['pack_credits'] ?? setting_value('min_credit_pack','20'));
$bonus = (float)($row['bonus_credits'] ?? setting_value('default_bonus_credits','0'));
$usd = (float)($row['pack_price_usd'] ?? ((float)setting_value('credit_price','0.50') * $credits));
$prevUsd = (float)($row['previous_price_usd'] ?? 0);
if ($prevUsd <= $usd) $prevUsd = round($usd * 1.9, 2);
$rate = (float)($row['fx_rate_override'] ?? 0);
if ($rate <= 0) $rate = (float)$geo['fx_rate'];
$symbol = $row['currency_symbol'] ?? $geo['currency_symbol'];
$code = $row['currency_code'] ?? $geo['currency_code'];
$local = $usd * $rate;
$prevLocal = $prevUsd * $rate;
$totalCredits = $credits + $bonus;
$badge = trim((string)($row['badge_text'] ?? setting_value('default_pack_badge','MOST POPULAR')));
$savingsPct = ($prevUsd > 0) ? max(0, min(95, round((1 - ($usd / $prevUsd)) * 100))) : 0;
return [
'country_code'=>$geo['country_code'], 'language'=>$geo['language'],
'credits'=>$credits, 'bonus_credits'=>$bonus, 'total_credits'=>$totalCredits,
'pay_usd'=>round($usd,2), 'paypal_amount_usd'=>round($usd,2), 'previous_price_usd'=>round($prevUsd,2),
'local_amount'=>round($local,2), 'previous_local_amount'=>round($prevLocal,2),
'currency_code'=>$code, 'currency_symbol'=>$symbol, 'badge_text'=>$badge, 'savings_percent'=>$savingsPct,
'matches'=>$totalCredits, 'featured'=> (int)($row['is_featured'] ?? 1),
// Local currency is DISPLAY ONLY. PayPal/order amount remains exactly the USD value below.
'display_text'=>trim($symbol.' '.number_format($local, ($local>=1000?0:2))).' ≈ $'.number_format($usd,2).' USD',
'previous_display_text'=>trim($symbol.' '.number_format($prevLocal, ($prevLocal>=1000?0:2))).' ≈ $'.number_format($prevUsd,2).' USD',
'small_paypal_text'=>'PayPal checkout amount: $'.number_format($usd,2).' USD',
'paypal_notice'=>'You will pay exactly $'.number_format($usd,2).' USD in PayPal. Local currency is only an estimated display using FX rate '.number_format($rate,4).'.',
'fx_rate_used'=>round($rate,6),
'local_display_only'=>true
];
}
function pooliva_language_for_country(?string $cc=null): string {
$geo = pooliva_geo_profile($cc);
$force = setting_value('force_language','auto');
if ($force && $force !== 'auto') return $force;
if (setting_value('auto_language_enabled','1') !== '1') return 'en';
// Browser language gets priority when the visitor's device clearly asks for it.
$accept = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
if ($accept) {
$first = strtolower(trim(explode(',', $accept)[0]));
$first = str_replace('_','-', $first);
$catalog = pooliva_language_catalog();
if (isset($catalog[$first])) return $first;
$base = explode('-', $first)[0];
if (isset($catalog[$base])) return $base;
}
return $geo['language'] ?: 'en';
}
function pooliva_base_translation_en(): array {
return [
'enter_title'=>'Enter Pooliva','enter_desc'=>'Write your player name first. No signup. Your game profile stays linked to this device/IP.','name_placeholder'=>'Your player name','terms'=>'I agree to the name rules, privacy policy, and fair play terms.','start'=>'Start Playing','optional_photo'=>'Profile photo is optional after you enter.','tag'=>'🎱 First match free • instant device profile','hero_title'=>'Instant Pool Battles','hero_desc'=>'Enter in seconds, beat global players, build streaks, and push your country up the City Wars board.','play_free'=>'Play Free Match','play_next'=>'Play Next Match','buy'=>'Buy Credits','edit_profile'=>'Edit Name / Photo','city_theme'=>'🏙️ City Table Theme','leaderboard'=>'Global Leaderboard','city_wars'=>'🌍 City Wars'
];
}
function pooliva_static_translations(): array {
return [
'en'=>pooliva_base_translation_en(),
'ar'=>['enter_title'=>'ادخل Pooliva','enter_desc'=>'اكتب اسم اللاعب أولاً. بدون تسجيل. ملفك يبقى مربوط بجهازك.','name_placeholder'=>'اسم اللاعب','terms'=>'أوافق على شروط الاسم والخصوصية واللعب العادل.','start'=>'ابدأ اللعب','optional_photo'=>'صورة البروفايل اختيارية بعد الدخول.','tag'=>'🎱 أول مباراة مجانية • ملف فوري للجهاز','hero_title'=>'تحديات بلياردو فورية','hero_desc'=>'ادخل خلال ثواني، اهزم لاعبين عالميين، ارفع الستريك وادفع بلدك في ترتيب City Wars.','play_free'=>'العب مباراة مجانية','play_next'=>'العب المباراة التالية','buy'=>'شراء كريدت','edit_profile'=>'تعديل الاسم / الصورة','city_theme'=>'🏙️ ثيم طاولة المدينة','leaderboard'=>'الترتيب العالمي','city_wars'=>'🌍 حروب المدن'],
'es'=>['enter_title'=>'Entra a Pooliva','enter_desc'=>'Escribe tu nombre primero. Sin registro. Tu perfil queda ligado a este dispositivo.','name_placeholder'=>'Tu nombre','terms'=>'Acepto las reglas del nombre, privacidad y juego limpio.','start'=>'Empezar','optional_photo'=>'La foto de perfil es opcional después.','tag'=>'🎱 Primera partida gratis • perfil instantáneo','hero_title'=>'Batallas de pool instantáneas','hero_desc'=>'Entra en segundos, vence jugadores globales y sube tu país en City Wars.','play_free'=>'Jugar gratis','play_next'=>'Siguiente partida','buy'=>'Comprar créditos','edit_profile'=>'Editar nombre / foto','city_theme'=>'🏙️ Tema de mesa','leaderboard'=>'Ranking global','city_wars'=>'🌍 City Wars'],
'pt'=>['enter_title'=>'Entre no Pooliva','enter_desc'=>'Digite seu nome primeiro. Sem cadastro. Seu perfil fica ligado a este dispositivo.','name_placeholder'=>'Seu nome','terms'=>'Aceito as regras de nome, privacidade e jogo justo.','start'=>'Começar','optional_photo'=>'Foto de perfil é opcional depois.','tag'=>'🎱 Primeira partida grátis • perfil instantâneo','hero_title'=>'Batalhas instantâneas de sinuca','hero_desc'=>'Entre em segundos, vença jogadores globais e leve seu país ao topo.','play_free'=>'Jogar grátis','play_next'=>'Próxima partida','buy'=>'Comprar créditos','edit_profile'=>'Editar nome / foto','city_theme'=>'🏙️ Tema da mesa','leaderboard'=>'Ranking global','city_wars'=>'🌍 City Wars']
];
}
function pooliva_ai_translate_bundle(string $lang): ?array {
if (setting_value('ai_translation_enabled','0') !== '1') return null;
$key = trim(setting_value('openai_api_key',''));
if (!$key || strtolower($key)==='null') return null;
$catalog = pooliva_language_catalog();
$target = $catalog[$lang] ?? $lang;
$base = pooliva_base_translation_en();
$model = setting_value('translation_model','gpt-4o-mini');
$note = setting_value('translation_quality_note','short, natural mobile pool game UI text. Keep Pooliva, City Wars, Credits as brand/game terms when natural. Return JSON only.');
$payload = json_encode([
'model'=>$model,
'messages'=>[
['role'=>'system','content'=>'You translate short mobile game UI strings. Return only valid compact JSON object with the same keys. No markdown.'],
['role'=>'user','content'=>'Translate this Pooliva game UI into '.$target.' ('.$lang.'). '.$note.' JSON: '.json_encode($base, JSON_UNESCAPED_UNICODE)]
],
'temperature'=>0.2
], JSON_UNESCAPED_UNICODE);
try {
$ch = curl_init('https://api.openai.com/v1/chat/completions');
curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,CURLOPT_HTTPHEADER=>['Content-Type: application/json','Authorization: Bearer '.$key],CURLOPT_POSTFIELDS=>$payload,CURLOPT_TIMEOUT=>18]);
$res = curl_exec($ch); $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if (!$res || $code < 200 || $code >= 300) return null;
$j=json_decode($res,true); $txt=$j['choices'][0]['message']['content'] ?? '';
$arr=json_decode($txt,true); if (!is_array($arr)) return null;
return array_intersect_key($arr, $base) + $base;
} catch(Throwable $e){ return null; }
}
function pooliva_translations(string $lang): array {
global $pdo;
$lang = str_replace('_','-', trim($lang ?: 'en'));
$static = pooliva_static_translations();
$base = pooliva_base_translation_en();
$out = $static[$lang] ?? $static[explode('-', $lang)[0] ?? 'en'] ?? $base;
try {
$st=$pdo->prepare('SELECT text_key,text_value FROM translations WHERE lang_code=?'); $st->execute([$lang]);
$rows=$st->fetchAll();
if (!$rows && !isset($static[$lang]) && setting_value('ai_translation_enabled','0')==='1') {
$ai = pooliva_ai_translate_bundle($lang);
if ($ai) {
$ins=$pdo->prepare('INSERT INTO translations(lang_code,text_key,text_value,source) VALUES(?,?,?,?) ON DUPLICATE KEY UPDATE text_value=VALUES(text_value),source=VALUES(source),updated_at=NOW()');
foreach($ai as $k=>$v){ $ins->execute([$lang,$k,$v,'ai-cache']); }
$out = $ai;
}
} else {
foreach($rows as $r){ $out[$r['text_key']]=$r['text_value']; }
}
} catch(Throwable $e) {}
return $out + $base;
}
Privacy Policy - Pooliva.net
Privacy Policy Effective date: 2026-05-23 • Version: 1.0
This Privacy Policy explains how REDLIVE LTD in the United Kingdom handles data for Pooliva.net.
1. Information we collect Player name, optional profile image, chat messages, voice taunts and gameplay activity. Device/browser identifiers, cookies/local storage tokens, device fingerprint signals, IP address or hashed IP, country code, language and approximate location from IP. Credits, purchases, payment references, game results, rewards, leaderboard data and invite links. Security logs, anti-cheat flags, VPN/proxy checks, rate-limit events, admin actions and technical error logs. 2. Why we use it We use data to provide gameplay, identify returning guest devices, prevent free-match abuse, process credits, run leaderboards, enable invites, protect against cheating, secure uploads, detect suspicious activity, improve performance, and comply with legal or payment-provider requirements.
3. Payments Payments may be handled by external payment providers such as PayPal or other processors. We do not intend to store full card details on our servers. Payment providers may process your data under their own policies.
4. Cookies, local storage and fingerprinting Because the game works without registration, we use cookies/local storage and device signals to keep your guest profile, credits, free-match status and security history tied to your device. Changing IP or VPN does not guarantee a new free profile.
5. Uploads and communications Profile images, voice taunts and chat content may be stored, scanned, restricted, logged or removed for safety, moderation, legal protection and abuse prevention.
6. Sharing We may share limited data with hosting, analytics, payment, security, anti-fraud, VPN/proxy detection, translation, email or infrastructure providers where needed to operate the service. We may also disclose data if required by law or to protect rights, safety and security.
7. Retention We keep data as long as needed for gameplay, fraud prevention, accounting, security, dispute handling, backups, legal protection and service improvement. Some logs may be retained longer where abuse, payment disputes or security risks are involved.
8. Your choices You can stop using the service, clear local storage/cookies, or contact the operator for reasonable privacy requests. Some data may need to be retained for security, payment, legal, anti-fraud or backup reasons.
9. Security We use measures such as upload restrictions, protected logs, CSRF/CSP protections, rate limits, database prepared statements, anti-cheat checks and admin security controls. No internet service can be guaranteed 100% secure.
10. Updates We may update this policy as the platform changes. Continued use means you accept the updated policy.
Back to game