CRM-13584: make it possible to localise the CiviCRM installer. Moves the language...
authorMathieu Lutfy <mathieu@bidon.ca>
Sun, 13 Oct 2013 18:51:23 +0000 (14:51 -0400)
committerMathieu Lutfy <mathieu@bidon.ca>
Sun, 13 Oct 2013 18:51:23 +0000 (14:51 -0400)
install/civicrm.php
install/index.php
install/template.html

index aade7071f8a8f5f10dbdff4b8048f62492f8965e..1f8e0492dd03e42d6ac9e6e33539779d35273c86 100644 (file)
@@ -36,12 +36,6 @@ function civicrm_setup($filesDirectory) {
   global $crmPath, $sqlPath, $pkgPath, $tplPath;
   global $compileDir;
 
-  $pkgPath = $crmPath . DIRECTORY_SEPARATOR . 'packages';
-  set_include_path($crmPath . PATH_SEPARATOR .
-    $pkgPath . PATH_SEPARATOR .
-    get_include_path()
-  );
-
   $sqlPath = $crmPath . DIRECTORY_SEPARATOR . 'sql';
   $tplPath = $crmPath . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'CRM' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR;
 
@@ -123,7 +117,6 @@ function civicrm_main(&$config) {
 
 function civicrm_source($dsn, $fileName, $lineMode = FALSE) {
   global $crmPath;
-
   require_once "$crmPath/packages/DB.php";
 
   $db = DB::connect($dsn);
index d70c360066be47a7fe3ae8a574d6c6df6304e30e..e6bf92efeada4eea81bcbac92719f5ac068a2d79 100644 (file)
@@ -58,8 +58,10 @@ if (!in_array($installType, array(
 }
 
 global $crmPath;
+global $pkgPath;
 global $installDirPath;
 global $installURLPath;
+
 if ($installType == 'drupal') {
   $crmPath = dirname(dirname($_SERVER['SCRIPT_FILENAME']));
   $installDirPath = $installURLPath = '';
@@ -67,11 +69,16 @@ if ($installType == 'drupal') {
 elseif ($installType == 'wordpress') {
   $crmPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR;
   $installDirPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
-
   $installURLPath = WP_PLUGIN_URL . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
 }
 
-set_include_path(get_include_path() . PATH_SEPARATOR . $crmPath);
+$pkgPath = $crmPath . DIRECTORY_SEPARATOR . 'packages';
+
+set_include_path(
+  $crmPath . PATH_SEPARATOR .
+  $pkgPath . PATH_SEPARATOR .
+  get_include_path()
+);
 
 require_once $crmPath . '/CRM/Core/ClassLoader.php';
 CRM_Core_ClassLoader::singleton()->register();
@@ -79,12 +86,10 @@ CRM_Core_ClassLoader::singleton()->register();
 $docLink = CRM_Utils_System::docURL2('Installation and Upgrades', FALSE, 'Installation Guide',NULL,NULL,"wiki");
 
 if ($installType == 'drupal') {
-  //lets check only /modules/.
+  // Lets check only /modules/.
   $pattern = '/' . preg_quote(CIVICRM_DIRECTORY_SEPARATOR . 'modules', CIVICRM_DIRECTORY_SEPARATOR) . '/';
 
-  if (!preg_match($pattern,
-      str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME'])
-    )) {
+  if (!preg_match($pattern, str_replace("\\", "/", $_SERVER['SCRIPT_FILENAME']))) {
     $errorTitle = "Oops! Please Correct Your Install Location";
     $errorMsg = "Please untar (uncompress) your downloaded copy of CiviCRM in the <strong>" . implode(CIVICRM_DIRECTORY_SEPARATOR, array(
       'sites', 'all', 'modules')) . "</strong> directory below your Drupal root directory. Refer to the online " . $docLink . " for more information.";
@@ -130,14 +135,30 @@ foreach ($langs as $locale => $_) {
   if ($locale == 'en_US') {
     continue;
   }
-  if (!file_exists(implode(CIVICRM_DIRECTORY_SEPARATOR, array($crmPath, 'sql', "civicrm_data.$locale.mysql"))))unset($langs[$locale]);
+  if (!file_exists(implode(CIVICRM_DIRECTORY_SEPARATOR, array($crmPath, 'sql', "civicrm_data.$locale.mysql")))) {
+    unset($langs[$locale]);
+  }
 }
 
+// Set the locale (works with both native gettext and phpgettext)
+define('CIVICRM_UF', 'Drupal');
+define('CIVICRM_GETTEXT_NATIVE', 1);
+global $tsLocale;
+
+$tsLocale = 'en_US';
 $seedLanguage = 'en_US';
+
 if (isset($_REQUEST['seedLanguage']) and isset($langs[$_REQUEST['seedLanguage']])) {
   $seedLanguage = $_REQUEST['seedLanguage'];
+  $tsLocale = $_REQUEST['seedLanguage'];
 }
 
+$config = CRM_Core_Config::singleton(FALSE);
+
+// The translation files are in the parent directory (l10n)
+$config->gettextResourceDir = '..' . DIRECTORY_SEPARATOR . $config->gettextResourceDir;
+$i18n = CRM_Core_I18n::singleton();
+
 global $cmsPath;
 if ($installType == 'drupal') {
   //CRM-6840 -don't force to install in sites/all/modules/
@@ -275,16 +296,16 @@ class InstallRequirements {
   function checkdatabase($databaseConfig, $dbName) {
     if ($this->requireFunction('mysql_connect',
         array(
-          "PHP Configuration",
-          "MySQL support",
-          "MySQL support not included in PHP.",
+          ts("PHP Configuration"),
+          ts("MySQL support"),
+          ts("MySQL support not included in PHP."),
         )
       )) {
       $this->requireMySQLServer($databaseConfig['server'],
         array(
-          "MySQL $dbName Configuration",
-          "Does the server exist",
-          "Can't find the a MySQL server on '$databaseConfig[server]'",
+          ts("MySQL %1 Configuration", array(1 => $dbName)),
+          ts("Does the server exist?"),
+          ts("Can't find the a MySQL server on '%1'", array(1 => $databaseConfig['server'])),
           $databaseConfig['server'],
         )
       );
@@ -292,15 +313,15 @@ class InstallRequirements {
           $databaseConfig['username'],
           $databaseConfig['password'],
           array(
-            "MySQL $dbName Configuration",
-            "Are the access credentials correct",
-            "That username/password doesn't work",
+            ts("MySQL %1 Configuration", array(1 => $dbName)),
+            ts("Are the access credentials correct?"),
+            ts("That username/password doesn't work"),
           )
         )) {
         @$this->requireMySQLVersion("5.1",
           array(
-            "MySQL $dbName Configuration",
-            "MySQL version at least 5.1",
+            ts("MySQL %1 Configuration", array(1 => $dbName)),
+            ts("MySQL version at least %1", array(1 => '5.1')),
             "MySQL version 5.1 or higher is required, you only have ",
             "MySQL " . mysql_get_server_info(),
           )
@@ -392,14 +413,14 @@ class InstallRequirements {
 
     $this->errors = NULL;
 
-    $this->requirePHPVersion('5.3.3', array("PHP Configuration", "PHP5 installed", NULL, "PHP version " . phpversion()));
+    $this->requirePHPVersion('5.3.3', array(ts("PHP Configuration"), ts("PHP5 installed"), NULL, ts("PHP version %1", array(1 => phpversion()))));
 
     // Check that we can identify the root folder successfully
     $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . 'README.txt',
       array(
-        "File permissions",
-        "Does the webserver know where files are stored?",
-        "The webserver isn't letting me identify where files are stored.",
+        ts("File permissions"),
+        ts("Does the webserver know where files are stored?"),
+        ts("The webserver isn't letting me identify where files are stored."),
         $this->getBaseDir(),
       ),
       TRUE
@@ -408,9 +429,9 @@ class InstallRequirements {
     // CRM-6485: make sure the path does not contain PATH_SEPARATOR, as we don’t know how to escape it
     $this->requireNoPathSeparator(
       array(
-        'File permissions',
-        'does the CiviCRM path contain PATH_SEPARATOR?',
-        'the ' . $this->getBaseDir() . ' path contains PATH_SEPARATOR (the ' . PATH_SEPARATOR . ' character)',
+        ts('File permissions'),
+        ts('Does the CiviCRM path contain PATH_SEPARATOR?'),
+        ts('The %1 path contains the PATH_SEPARATOR (the %2 character)', array(1 => $this->getBaseDir(), 2 => PATH_SEPARATOR)),
         $this->getBaseDir(),
       )
     );
@@ -419,7 +440,11 @@ class InstallRequirements {
     foreach ($requiredDirectories as $dir) {
       $this->requireFile($crmPath . CIVICRM_DIRECTORY_SEPARATOR . $dir,
         array(
-          "File permissions", "$dir folder exists", "There is no $dir folder"), TRUE
+          ts("File permissions"),
+          ts("%1: folder exists", array(1 => $dir)),
+          ts("%1: folder is missing", array(1 => $dir)),
+        ),
+        TRUE
       );
     }
 
@@ -447,7 +472,7 @@ class InstallRequirements {
     foreach ($writableDirectories as $dir) {
       $dirName = CIVICRM_WINDOWS ? $dir : CIVICRM_DIRECTORY_SEPARATOR . $dir;
       $this->requireWriteable($dirName,
-        array("File permissions", "Is the $dir folder writeable?", NULL),
+        array(ts("File permissions"), ts("Is the %1 folder writeable?", array(1 => $dir)), NULL),
         TRUE
       );
     }
@@ -471,7 +496,7 @@ class InstallRequirements {
     }
 
     if ($webserver == '') {
-      $webserver = "I can't tell what webserver you are running";
+      $webserver = ts("I can't tell what webserver you are running");
     }
 
     // Check for $_SERVER configuration
@@ -479,17 +504,17 @@ class InstallRequirements {
 
     // Check for MySQL support
     $this->requireFunction('mysql_connect',
-      array("PHP Configuration", "MySQL support", "MySQL support not included in PHP.")
+      array(ts("PHP Configuration"), ts("MySQL support"), ts("MySQL support not included in PHP."))
     );
 
     // Check for JSON support
     $this->requireFunction('json_encode',
-      array("PHP Configuration", "JSON support", "JSON support not included in PHP.")
+      array(ts("PHP Configuration"), ts("JSON support"), ts("JSON support not included in PHP."))
     );
 
     // Check for xcache_isset and emit warning if exists
     $this->checkXCache(array(
-      "PHP Configuration",
+      ts("PHP Configuration"),
         "XCache compatibility",
         "XCache is installed and there are known compatibility issues between XCache and CiviCRM. Consider using an alternative PHP caching mechanism or disable PHP caching altogether.",
       ));
@@ -498,9 +523,9 @@ class InstallRequirements {
     $this->requireMemory(32 * 1024 * 1024,
       64 * 1024 * 1024,
       array(
-        "PHP Configuration",
-        "Memory allocated (PHP config option 'memory_limit')",
-        "CiviCRM needs a minimum of 32M allocated to PHP, but recommends 64M.",
+        ts("PHP Configuration"),
+        ts("Memory allocated (PHP config option 'memory_limit')"),
+        ts("CiviCRM needs a minimum of 32M allocated to PHP, but recommends 64M."),
         ini_get("memory_limit"),
       )
     );
@@ -513,15 +538,15 @@ class InstallRequirements {
     $mem = $this->getPHPMemory();
 
     if ($mem < $min && $mem > 0) {
-      $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated";
+      $testDetails[2] .= " " . ts("You only have %1 of memory allocated", array(1 => ini_get("memory_limit")));
       $this->error($testDetails);
     }
     elseif ($mem < $recommended && $mem > 0) {
-      $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated";
+      $testDetails[2] .= " " . ts("You only have %1 of memory allocated", array(1 => ini_get("memory_limit")));
       $this->warning($testDetails);
     }
     elseif ($mem == 0) {
-      $testDetails[2] .= " We can't determine how much memory you have allocated. Install only if you're sure you've allocated at least 20 MB.";
+      $testDetails[2] .= " " . ts("We can't determine how much memory you have allocated. Install only if you're sure you've allocated at least 20 MB.");
       $this->warning($testDetails);
     }
   }
@@ -546,7 +571,7 @@ class InstallRequirements {
 
   function listErrors() {
     if ($this->errors) {
-      echo "<p>The following problems are preventing me from installing CiviCRM:</p>";
+      echo "<p>" . ts("The following problems are preventing me from installing CiviCRM:") . "</p>";
       foreach ($this->errors as $error) {
         echo "<li>" . htmlentities($error) . "</li>";
       }
index 6202e9ff296d60f8dccc370f7e0f84740bb89f9f..d9ace3f94def6627d4528d84efc7b370073b5e30 100644 (file)
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-<title>CiviCRM Installer</title>
+<title><?php echo ts('CiviCRM Installer'); ?></title>
 <link rel="stylesheet" type="text/css" href=<?php echo $installURLPath . "template.css"?> />
 </head>
 
 <body>
+
 <div id="All">
-    <div style="float: right"><b>Version <?php echo "{$civicrm_version['version']} {$civicrm_version['cms']}" ?></b></br>
+  <div style="float: right"><strong>Version <?php echo "{$civicrm_version['version']} {$civicrm_version['cms']}" ?></strong><br/>
     <span><img src=<?php echo $installURLPath . "block_small.png"?> /></span>
-    </div>
-<h1>CiviCRM Installer</h1>
-<div>Thanks for choosing to use CiviCRM!  Please follow the instructions below to get CiviCRM installed.</div>
-</br><br>
+  </div>
+
+<h1><?php echo ts("CiviCRM Installer"); ?></h1>
+<p><?php echo ts("Thanks for choosing CiviCRM! Please follow the instructions below to install CiviCRM."); ?></p>
+
 <form name="civicrm_form" method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
+
 <?php if(isset($hasErrorOtherThanDatabase)) { ?>
-<p class="error">We are not able to install the software.  Please <a style="color: #CCF" href="#requirements">see below</a> for details.</p>
+  <p class="error"><?php echo ts('We are not able to install the software. Please <a href="#requirements">see below</a> for details.'); ?></p>
 <?php } else { ?>
-<?php if($req->hasWarnings()) { ?>
-<p class="warning">There are some issues that we recommend you look at before installing, however, you are still able to install the software.  Please see below for details.</p>
+  <?php if($req->hasWarnings()) { ?>
+    <p class="warning"><?php echo ts('There are some issues that we recommend you look at before installing, however, you are still able to install the software.  Please see below for details.'); ?></p>
+  <?php } else if(!$dbReq->hasErrors()) { ?>
+    <p class="good"><?php echo ts("You're ready to install!"); ?></p>
+  <?php } ?>
 
-<?php } else if(!$dbReq->hasErrors()) { ?>
-<p class="good">You're ready to install! &nbsp;&nbsp;
-</p>
-
-<?php } ?>
-
-<p>
-  <input id="install_button" type="submit" name="go" value="Check Requirements and Install CiviCRM" onclick="document.getElementById('saving_top').style.display = ''; this.value = 'Installing CiviCRM...'" />
+  <p>
+  <input id="install_button" type="submit" name="go" value="<?php echo ts('Check Requirements and Install CiviCRM', array('escape' => 'js')); ?>" onclick="document.getElementById('saving_top').style.display = ''; this.value = '<?php echo ts('Installing CiviCRM...', array('escape' => 'js')); ?>'" />
 
   <span id="saving_top" style="display: none">
   &nbsp;
     <img src=<?php echo $installURLPath . "network-save.gif"?> />
-  (this will take a few minutes)
+  <?php echo ts('(this will take a few minutes)'); ?>
   </span>
-</p>
+  </p>
 <?php } ?>
 
+<h4><?php echo ts('Language and Region Settings'); ?></h4>
+
+<p><?php echo ts('CiviCRM has been translated to many languages, thanks to its community of translators. By selecting another language, the installer may be available in that language. The initial configuration of the basic data will also be set to that language (ex: individual prefixes, suffixes, activity types, etc.). <a href="%1">Learn more about using CiviCRM in other languages.</a>', array(1 => 'http://wiki.civicrm.org/confluence/pages/viewpage.action?pageId=88408149')); ?></p>
 
+<script>
+  function civicrmInstallerSetLanguage(language) {
+     var location = window.location.toString();
+
+     if (location.match(/seedLanguage=.._../)) {
+       location = location.replace(/seedLanguage=.._../, 'seedLanguage=' + language);
+       window.location = location;
+     }
+     else {
+       window.location += '?seedLanguage=' + language;
+     }
+  }
+</script>
+
+<p style="margin-left: 2em" id="locale">
+<label for="seedLanguage"><span><?php echo ts('Language of basic data:'); ?></span></label>
+<select id="seedLanguage" name="seedLanguage" onchange="civicrmInstallerSetLanguage(this.value);">
+  <?php
+    foreach ($langs as $locale => $language) {
+      $selected = ($locale == $seedLanguage) ? 'selected="selected"' : '';
+      echo "<option value='$locale' $selected>$language</option>";
+    }
+  ?>
+</select>
+<noscript>
+  <input type="submit" name="setlanguage" value="<?php echo ts('Change language', array('escape' => 'js')); ?>" />
+</noscript>
+<span class="testResults">
+  <?php
+    if (count($langs) < 2) {
+      echo "(download the civicrm-{$civicrm_version['version']}-l10n.tar.gz file and unzip into CiviCRM’s directory to add languages here)";
+    }
+  ?>
+</span>
+</p>
+
+<h4><?php echo ts('CiviCRM Database Settings'); ?></h4>
 <input type="hidden" name="database" value="MySQLDatabase" />
 
-<h2>Database Version and Connection Settings</h2>
 <p>
-CiviCRM stores its content in a MySQL database.  Please provide the username
-and password to connect to the server here.  If this account has permission to create databases, then we will
-create the database for you; otherwise, you must give the name of a database that already exists.
+<?php echo ts('CiviCRM stores its content in a MySQL database. Please provide the username and password to connect to the server here.  If this account has permission to create databases, then we will create the database for you; otherwise, you must give the name of a database that already exists.'); ?>
 </p>
 
 <?php if($dbReq->hasErrors()) { ?>
-<p class="error">Your database settings don't appear to be correct. Please check the <strong><a href="#dbDetails" style="color: white;">Database Details</a></strong> below for specific errors.</p>
+  <p class="error"><?php echo ts("Your database settings don't appear to be correct. Please check the <a href='%1'>Database Details</a> below for specific errors.", array(1 => '#dbDetails')); ?></p>
 <?php } else { ?>
-<p class="good">Database version and connection settings have been verified and look correct!</p>
+  <p class="good"><?php echo ts("Database version and connection settings have been verified and look correct!"); ?></p>
 <?php } ?>
 
-<h4>CiviCRM Database Settings</h4>
-<p style="margin-left: 2em" id="mysql_credentials" > <!--style="display: none"-->
-  <label for="mysql_server"> <span>MySQL server:</span> <input id="mysql_server" type="text" name="mysql[server]" value="<?php echo $databaseConfig['server'] ?>" /></label> <br />
-  <label for="mysql_username"> <span>MySQL username:</span> <input id="mysql_username" type="text" name="mysql[username]" value="<?php echo $databaseConfig['username'] ?>"  /></label> <br />
-  <label for="mysql_password"> <span>MySQL password:</span> <input id="mysql_password" type="password" name="mysql[password]" value="<?php echo $databaseConfig['password'] ?>" /></label> <br />
-  <label for="mysql_database"><span>MySQL database:</span> <input id="mysql_database" type="text" name="mysql[database]" value="<?php echo $databaseConfig['database'] ?>" /></label> <br />
+<p style="margin-left: 2em" id="mysql_credentials" >
+  <label for="mysql_server"> <span><?php echo ts('MySQL server:'); ?></span> <input id="mysql_server" type="text" name="mysql[server]" value="<?php echo $databaseConfig['server'] ?>" /></label> <br />
+  <label for="mysql_username"> <span><?php echo ts('MySQL username:'); ?></span> <input id="mysql_username" type="text" name="mysql[username]" value="<?php echo $databaseConfig['username'] ?>"  /></label> <br />
+  <label for="mysql_password"> <span><?php echo ts('MySQL password:'); ?></span> <input id="mysql_password" type="password" name="mysql[password]" value="<?php echo $databaseConfig['password'] ?>" /></label> <br />
+  <label for="mysql_database"><span><?php echo ts('MySQL database:'); ?></span> <input id="mysql_database" type="text" name="mysql[database]" value="<?php echo $databaseConfig['database'] ?>" /></label> <br />
 </p>
 
 <?php if ($installType == 'drupal') { ?>
-<h4>Drupal Database Settings</h4>
+<h4><?php echo ts('Drupal Database Settings'); ?></h4>
 <p style="margin-left: 2em" id="drupal_credentials" > <!--style="display: none"-->
-  <label for="drupal_server"> <span>MySQL server:</span> <input id="drupal_server" type="text" name="drupal[server]" value="<?php echo $drupalConfig['server'] ?>" /></label> <br />
-  <label for="drupal_username"> <span>MySQL username:</span> <input id="drupal_username" type="text" name="drupal[username]" value="<?php echo $drupalConfig['username'] ?>"  /></label> <br />
-  <label for="drupal_password"> <span>MySQL password:</span> <input id="drupal_password" type="password" name="drupal[password]" value="<?php echo $drupalConfig['password'] ?>" /></label> <br />
-  <label for="drupal_database"><span>MySQL database:</span> <input id="drupal_database" type="text" name="drupal[database]" value="<?php echo $drupalConfig['database'] ?>" /></label> <br />
+  <label for="drupal_server"> <span><?php echo ts('MySQL server:'); ?></span> <input id="drupal_server" type="text" name="drupal[server]" value="<?php echo $drupalConfig['server'] ?>" /></label> <br />
+  <label for="drupal_username"> <span><?php echo ts('MySQL username:'); ?></span> <input id="drupal_username" type="text" name="drupal[username]" value="<?php echo $drupalConfig['username'] ?>"  /></label> <br />
+  <label for="drupal_password"> <span><?php echo ts('MySQL password:'); ?></span> <input id="drupal_password" type="password" name="drupal[password]" value="<?php echo $drupalConfig['password'] ?>" /></label> <br />
+  <label for="drupal_database"><span><?php echo ts('MySQL database:'); ?></span> <input id="drupal_database" type="text" name="drupal[database]" value="<?php echo $drupalConfig['database'] ?>" /></label> <br />
 </p>
 <?php } ?>
 
 
-<h4>Other Settings</h4>
-<p style="margin-left: 2em" id="misc_credentials" >
-<!--style="display: none"-->
-<label for="seedLanguage"><span>Language of basic data:</span></label><select id="seedLanguage" name="seedLanguage">
-  <?php
-    foreach ($langs as $locale => $language) {
-      $selected = ($locale == $seedLanguage) ? 'selected="selected"' : '';
-      echo "<option value='$locale' $selected>$language</option>";
-    }
-  ?>
-</select>
-<span class="testResults">
-  <?php
-    if (count($langs) < 2) {
-      echo "(download the civicrm-{$civicrm_version['version']}-l10n.tar.gz file and unzip into CiviCRM’s directory to add languages here)";
-    }
-  ?>
-</span><br />
+<h4><?php echo ts('Other Settings'); ?></h4>
+
+<p>
     <label for="loadGenerated"><span>Load sample data:</span><input id="loadGenerated" type="checkbox" name="loadGenerated" value=1 <?php if ( $loadGenerated == 1 ) { echo "checked='checked'"; } ?> /></label> <br />
     <span class="testResults">Check this box to pre-populate CiviCRM with sample English contact records, online contribution pages, profile forms, etc. These examples can help you learn about CiviCRM features.</span><br />
 </p>