1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-08-08 22:36:41 +02:00

tools/text-translator: new extractor tool & README.md

This commit is contained in:
jsign
2019-08-20 21:30:23 -03:00
parent 4ca29e4ad5
commit 62f9cff7fe
8 changed files with 785 additions and 53 deletions

View File

@@ -0,0 +1,251 @@
{{define "title"}}Create an Account{{end}}
{{define "description"}}Sign Up for free to our Software-as-a-Service solution. {{end}}
{{define "style"}}
{{end}}
{{ define "partials/app-wrapper" }}
<div class="container" id="page-content">
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<!-- Nested Row within Card Body -->
<div class="row">
<div class="col-lg-5 d-none d-lg-block bg-register-image"></div>
<div class="col-lg-7">
<div class="p-5">
{{ template "app-flashes" . }}
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">Create an Account!</h1>
</div>
{{ template "validation-error" . }}
<hr>
<form class="user" method="post" novalidate>
<div>
<h2 class="h5 text-gray-900 mt-3 mb-3">Your Organization details</h2>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Name" }}"
name="Account.Name" value="{{ $.form.Account.Name }}" placeholder="Company Name" required>
{{template "invalid-feedback" dict "fieldName" "Account.Name" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Address1" }}"
name="Account.Address1" value="{{ $.form.Account.Address1 }}" placeholder="Address Line 1" required>
{{template "invalid-feedback" dict "fieldName" "Account.Address1" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Address2" }}"
name="Account.Address2" value="{{ $.form.Account.Address2 }}" placeholder="Address Line 2">
{{template "invalid-feedback" dict "fieldName" "Account.Address2" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<div class="form-control-select-wrapper">
<select id="selectAccountCountry" name="Account.Country" placeholder="Country" required
class="form-control form-control-select-box {{ ValidationFieldClass $.validationErrors "Account.Country" }}">
{{ range $i := $.countries }}
{{ $hasGeonames := false }}
{{ range $c := $.geonameCountries }}
{{ if eq $c $i.Code }}{{ $hasGeonames = true }}{{ end }}
{{ end }}
<option value="{{ $i.Code }}" data-geonames="{{ if $hasGeonames }}1{{ else }}0{{ end }}" {{ if eq $.form.Account.Country $i.Code }}selected="selected"{{ end }}>{{ $i.Name }}</option>
{{ end }}
</select>
{{template "invalid-feedback" dict "fieldName" "Account.Country" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<div id="divAccountZipcode"></div>
{{template "invalid-feedback" dict "fieldName" "Account.Zipcode" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6 mb-3 mb-sm-0">
<div id="divAccountRegion"></div>
{{template "invalid-feedback" dict "fieldName" "Account.Region" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group row mb-4">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text" id="inputAccountCity"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.City" }}"
name="Account.City" value="{{ $.form.Account.City }}" placeholder="City" required>
{{template "invalid-feedback" dict "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors "fieldName" "Account.City" }}
</div>
<!-- div class="col-sm-6 mb-3 mb-sm-0">
<select class="form-control {{ ValidationFieldClass $.validationErrors "Account.Timezone" }}" id="selectAccountTimezone" name="Account.Timezone" placeholder="Timezone"></select>
{{template "invalid-feedback" dict "fieldName" "Account.Timezone" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div -->
</div>
<hr>
<div>
<h2 class="h5 text-gray-900 mt-3 mb-3">Your User details</h2>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.FirstName" }}"
name="User.FirstName" value="{{ $.form.User.FirstName }}" placeholder="First Name" required>
{{template "invalid-feedback" dict "fieldName" "User.FirstName" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.LastName" }}"
name="User.LastName" value="{{ $.form.User.LastName }}" placeholder="Last Name" required>
{{template "invalid-feedback" dict "fieldName" "User.LastName" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group">
<input type="email"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.Email" }}"
name="User.Email" value="{{ $.form.User.Email }}" placeholder="Email Address" required>
{{template "invalid-feedback" dict "fieldName" "User.Email" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="password"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.Password" }}"
name="User.Password" value="{{ $.form.User.Password }}" placeholder="Password" required>
{{template "invalid-feedback" dict "fieldName" "User.Password" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6">
<input type="password"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.PasswordConfirm" }}"
name="User.PasswordConfirm" value="{{ $.form.User.PasswordConfirm }}" placeholder="Repeat Password" required>
{{template "invalid-feedback" dict "fieldName" "User.PasswordConfirm" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<button class="btn btn-primary btn-user btn-block">
Register Account
</button>
</form>
<hr>
<div class="text-center">
<a class="small" href="/user/login">Already have an account? Login!</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}}
{{define "js"}}
<script src="https://cdn.jsdelivr.net/gh/xcash/bootstrap-autocomplete@v2.2.2/dist/latest/bootstrap-autocomplete.min.js"></script>
<script>
$(document).ready(function() {
$(document).find('body').addClass('bg-gradient-primary');
$('#selectAccountCountry').on('change', function () {
// When a country has data-geonames, then we can perform autocomplete on zipcode and
// populate a list of valid regions.
if ($(this).find('option:selected').attr('data-geonames') == 1) {
// Replace the existing region with an empty dropdown.
$('#divAccountRegion').html('<div class="form-control-select-wrapper"><select class="form-control form-control-select-box {{ ValidationFieldClass $.validationErrors "Account.Region" }}" id="inputAccountRegion" name="Account.Region" value="{{ $.form.Account.Region }}" placeholder="Region" required></select></div>');
// Query the API for a list of regions for the selected
// country and populate the region dropdown.
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/regions/autocomplete',
data: {country_code: $(this).val(), select: true},
dataType: 'json'
}).done(function (res) {
if (res !== undefined && res !== null) {
for (var c in res) {
$('#inputAccountRegion').append('<option value="'+res[c].value+'">'+res[c].text+'</option>');
}
}
});
/*
// Remove all the existing items from the timezone dropdown and repopulate it.
$('#selectAccountTimezone').find('option').remove().end()
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/country/'+$(this).val()+'/timezones',
data: {},
dataType: 'json'
}).done(function (res) {
if (res !== undefined && res !== null) {
for (var c in res) {
$('#selectAccountTimezone').append('<option value="'+res[c]+'">'+res[c]+'</option>');
}
}
});
*/
// Replace the existing zipcode text input with a new one that will supports autocomplete.
$('#divAccountZipcode').html('<input class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Zipcode" }}" id="inputAccountZipcode" name="Account.Zipcode" value="{{ $.form.Account.Zipcode }}" placeholder="Zipcode" required>');
$('#inputAccountZipcode').autoComplete({
minLength: 2,
events: {
search: function (qry, callback) {
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/postal_codes/autocomplete',
data: {query: qry, country_code: $('#selectAccountCountry').val()},
dataType: 'json'
}).done(function (res) {
callback(res)
});
}
}
});
// When the value of zipcode changes, try to find an exact match for the zipcode and
// can therefore set the correct region and city.
$('#inputAccountZipcode').on('change', function() {
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/geonames/postal_code/'+$(this).val(),
data: {country_code: $('#selectAccountCountry').val()},
dataType: 'json'
}).done(function (res) {
if (res !== undefined && res !== null && res.PostalCode !== undefined) {
$('#inputAccountCity').val(res.PlaceName);
$('#inputAccountRegion').val(res.StateCode);
}
});
});
} else {
// Replace the existing zipcode input with no autocomplete.
$('#divAccountZipcode').html('<input type="text" class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Zipcode" }}" id="inputAccountZipcode" name="Account.Zipcode" value="{{ $.form.Account.Zipcode }}" placeholder="Zipcode" required>');
// Replace the existing region select with a text input.
$('#divAccountRegion').html('<input type="text" class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Region" }}" id="inputAccountRegion" name="Account.Region" value="{{ $.form.Account.Region }}" placeholder="Region" required>');
}
}).change();
hideDuplicateValidationFieldErrors();
});
</script>
{{end}}

View File

@@ -0,0 +1,251 @@
{{define "title"}}{{ $.trans.T "signup-step1-title" }}{{end}}
{{define "description"}}{{ $.trans.T "signup-step1-description" }}{{end}}
{{define "style"}}
{{end}}
{{ define "partials/app-wrapper" }}
<div class="container" id="page-content">
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<!-- Nested Row within Card Body -->
<div class="row">
<div class="col-lg-5 d-none d-lg-block bg-register-image"></div>
<div class="col-lg-7">
<div class="p-5">
{{ template "app-flashes" . }}
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">{{ $.trans.T "signup-step1-create-an-account" }}</h1>
</div>
{{ template "validation-error" . }}
<hr>
<form class="user" method="post" novalidate>
<div>
<h2 class="h5 text-gray-900 mt-3 mb-3">{{ $.trans.T "signup-step1-your-organization-details" }}</h2>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Name" }}"
name="Account.Name" value="{{ $.form.Account.Name }}" placeholder="{{ $.trans.T "signup-step1-company-name" }}" required>
{{template "invalid-feedback" dict "fieldName" "Account.Name" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Address1" }}"
name="Account.Address1" value="{{ $.form.Account.Address1 }}" placeholder="{{ $.trans.T "signup-step1-address-line-1" }}" required>
{{template "invalid-feedback" dict "fieldName" "Account.Address1" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Address2" }}"
name="Account.Address2" value="{{ $.form.Account.Address2 }}" placeholder="{{ $.trans.T "signup-step1-address-line-2" }}">
{{template "invalid-feedback" dict "fieldName" "Account.Address2" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<div class="form-control-select-wrapper">
<select id="selectAccountCountry" name="Account.Country" placeholder="{{ $.trans.T "signup-step1-country" }}" required
class="form-control form-control-select-box {{ ValidationFieldClass $.validationErrors "Account.Country" }}">
{{ range $i := $.countries }}
{{ $hasGeonames := false }}
{{ range $c := $.geonameCountries }}
{{ if eq $c $i.Code }}{{ $hasGeonames = true }}{{ end }}
{{ end }}
<option value="{{ $i.Code }}" data-geonames="{{ if $hasGeonames }}1{{ else }}0{{ end }}" {{ if eq $.form.Account.Country $i.Code }}selected="selected"{{ end }}>{{ $i.Name }}</option>
{{ end }}
</select>
{{template "invalid-feedback" dict "fieldName" "Account.Country" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<div id="divAccountZipcode"></div>
{{template "invalid-feedback" dict "fieldName" "Account.Zipcode" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6 mb-3 mb-sm-0">
<div id="divAccountRegion"></div>
{{template "invalid-feedback" dict "fieldName" "Account.Region" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group row mb-4">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text" id="inputAccountCity"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.City" }}"
name="Account.City" value="{{ $.form.Account.City }}" placeholder="{{ $.trans.T "signup-step1-city" }}" required>
{{template "invalid-feedback" dict "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors "fieldName" "Account.City" }}
</div>
<!-- div class="col-sm-6 mb-3 mb-sm-0">
<select class="form-control {{ ValidationFieldClass $.validationErrors "Account.Timezone" }}" id="selectAccountTimezone" name="Account.Timezone" placeholder="Timezone"></select>
{{template "invalid-feedback" dict "fieldName" "Account.Timezone" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div -->
</div>
<hr>
<div>
<h2 class="h5 text-gray-900 mt-3 mb-3">{{ $.trans.T "signup-step1-your-user-details" }}</h2>
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.FirstName" }}"
name="User.FirstName" value="{{ $.form.User.FirstName }}" placeholder="{{ $.trans.T "signup-step1-first-name" }}" required>
{{template "invalid-feedback" dict "fieldName" "User.FirstName" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6">
<input type="text"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.LastName" }}"
name="User.LastName" value="{{ $.form.User.LastName }}" placeholder="{{ $.trans.T "signup-step1-last-name" }}" required>
{{template "invalid-feedback" dict "fieldName" "User.LastName" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<div class="form-group">
<input type="email"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.Email" }}"
name="User.Email" value="{{ $.form.User.Email }}" placeholder="{{ $.trans.T "signup-step1-email-address" }}" required>
{{template "invalid-feedback" dict "fieldName" "User.Email" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="password"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.Password" }}"
name="User.Password" value="{{ $.form.User.Password }}" placeholder="{{ $.trans.T "signup-step1-password" }}" required>
{{template "invalid-feedback" dict "fieldName" "User.Password" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
<div class="col-sm-6">
<input type="password"
class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "User.PasswordConfirm" }}"
name="User.PasswordConfirm" value="{{ $.form.User.PasswordConfirm }}" placeholder="{{ $.trans.T "signup-step1-repeat-password" }}" required>
{{template "invalid-feedback" dict "fieldName" "User.PasswordConfirm" "validationDefaults" $.validationDefaults "validationErrors" $.validationErrors }}
</div>
</div>
<button class="btn btn-primary btn-user btn-block">
{{ $.trans.T "signup-step1-register-account" }}
</button>
</form>
<hr>
<div class="text-center">
<a class="small" href="/user/login">{{ $.trans.T "signup-step1-already-have-an-account" }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}}
{{define "js"}}
<script src="https://cdn.jsdelivr.net/gh/xcash/bootstrap-autocomplete@v2.2.2/dist/latest/bootstrap-autocomplete.min.js"></script>
<script>
$(document).ready(function() {
$(document).find('body').addClass('bg-gradient-primary');
$('#selectAccountCountry').on('change', function () {
// When a country has data-geonames, then we can perform autocomplete on zipcode and
// populate a list of valid regions.
if ($(this).find('option:selected').attr('data-geonames') == 1) {
// Replace the existing region with an empty dropdown.
$('#divAccountRegion').html('<div class="form-control-select-wrapper"><select class="form-control form-control-select-box {{ ValidationFieldClass $.validationErrors "Account.Region" }}" id="inputAccountRegion" name="Account.Region" value="{{ $.form.Account.Region }}" placeholder="Region" required></select></div>');
// Query the API for a list of regions for the selected
// country and populate the region dropdown.
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/regions/autocomplete',
data: {country_code: $(this).val(), select: true},
dataType: 'json'
}).done(function (res) {
if (res !== undefined && res !== null) {
for (var c in res) {
$('#inputAccountRegion').append('<option value="'+res[c].value+'">'+res[c].text+'</option>');
}
}
});
/*
// Remove all the existing items from the timezone dropdown and repopulate it.
$('#selectAccountTimezone').find('option').remove().end()
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/country/'+$(this).val()+'/timezones',
data: {},
dataType: 'json'
}).done(function (res) {
if (res !== undefined && res !== null) {
for (var c in res) {
$('#selectAccountTimezone').append('<option value="'+res[c]+'">'+res[c]+'</option>');
}
}
});
*/
// Replace the existing zipcode text input with a new one that will supports autocomplete.
$('#divAccountZipcode').html('<input class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Zipcode" }}" id="inputAccountZipcode" name="Account.Zipcode" value="{{ $.form.Account.Zipcode }}" placeholder="Zipcode" required>');
$('#inputAccountZipcode').autoComplete({
minLength: 2,
events: {
search: function (qry, callback) {
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/postal_codes/autocomplete',
data: {query: qry, country_code: $('#selectAccountCountry').val()},
dataType: 'json'
}).done(function (res) {
callback(res)
});
}
}
});
// When the value of zipcode changes, try to find an exact match for the zipcode and
// can therefore set the correct region and city.
$('#inputAccountZipcode').on('change', function() {
$.ajax({
type: 'GET',
contentType: 'application/json',
url: '/geo/geonames/postal_code/'+$(this).val(),
data: {country_code: $('#selectAccountCountry').val()},
dataType: 'json'
}).done(function (res) {
if (res !== undefined && res !== null && res.PostalCode !== undefined) {
$('#inputAccountCity').val(res.PlaceName);
$('#inputAccountRegion').val(res.StateCode);
}
});
});
} else {
// Replace the existing zipcode input with no autocomplete.
$('#divAccountZipcode').html('<input type="text" class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Zipcode" }}" id="inputAccountZipcode" name="Account.Zipcode" value="{{ $.form.Account.Zipcode }}" placeholder="Zipcode" required>');
// Replace the existing region select with a text input.
$('#divAccountRegion').html('<input type="text" class="form-control form-control-user {{ ValidationFieldClass $.validationErrors "Account.Region" }}" id="inputAccountRegion" name="Account.Region" value="{{ $.form.Account.Region }}" placeholder="Region" required>');
}
}).change();
hideDuplicateValidationFieldErrors();
});
</script>
{{end}}

View File

@@ -0,0 +1,196 @@
package main
import (
"flag"
"fmt"
"geeks-accelerator/oss/saas-starter-kit/tools/text-translator/internal/jsontranslator"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/PuerkitoBio/goquery"
)
const (
keyWordLengthLimit = 4
)
var (
inFile = flag.String("i", "", "source .gohtml file to extract text from")
outDir = flag.String("o", "", "output directory for translations")
locale = flag.String("l", "en", "locale of input file")
allowedCharsKeyRegex, _ = regexp.Compile("[^a-zA-Z0-9 ]+")
)
func main() {
flag.Parse()
flag.VisitAll(func(f *flag.Flag) {
if f.Value.String() == "" {
fmt.Printf("-%s flag is required\n", f.Name)
os.Exit(1)
}
})
s, err := os.Stat(*inFile)
if err != nil {
fmt.Printf("coudn't check if path is a file or directory: %v\n", err)
os.Exit(1)
}
if s.IsDir() {
filepath.Walk(*inFile, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("error while walking path: %v\n", err)
os.Exit(1)
}
if !info.IsDir() {
parseFile(path)
}
return nil
})
} else {
parseFile(*inFile)
}
}
func parseFile(path string) {
log.Printf("reading file %s\n", path)
b, err := ioutil.ReadFile(path)
if err != nil {
fmt.Printf("error while reading input file: %v\n", err)
os.Exit(1)
}
content := string(b)
_, name := filepath.Split(path)
filenameWithoutExt := strings.TrimRight(name, filepath.Ext(name))
translationFile := jsontranslator.JSONTranslation{Locale: *locale}
// Extract title and description
title, description := extractTitleAndDescription(content)
if title != "" {
translationFile.Items = append(translationFile.Items, jsontranslator.Translation{
Locale: *locale,
Key: fmt.Sprintf("%s-title", filenameWithoutExt),
Translation: title,
})
}
if description != "" {
translationFile.Items = append(translationFile.Items, jsontranslator.Translation{
Locale: *locale,
Key: fmt.Sprintf("%s-description", filenameWithoutExt),
Translation: description,
})
}
// Extract texts from html tags
extractedTexts := unique(extract(content, filenameWithoutExt))
for _, text := range extractedTexts {
key := makeKey(text, filenameWithoutExt)
translationFile.Items = append(translationFile.Items, jsontranslator.Translation{
Locale: *locale,
Key: key,
Translation: text,
})
}
err = jsontranslator.Save(*outDir, filenameWithoutExt+".json", []jsontranslator.JSONTranslation{translationFile})
if err != nil {
fmt.Printf("error while saving the extracted strings: %v\n", err)
os.Exit(1)
}
}
func extract(content string, keyPrefix string) []string {
var res []string
r := strings.NewReader(content)
doc, err := goquery.NewDocumentFromReader(r)
if err != nil {
log.Fatal(err)
}
f := func(text string) {
// Safemode: avoid contents that involve template actions
if strings.Index(text, "{{") != -1 {
return
}
if len(text) > 0 {
res = append(res, text)
}
}
// Extract <input> placeholder texts
doc.Find("input").Union(doc.Find("select")).Each(func(i int, input *goquery.Selection) {
if p, exists := input.Attr("placeholder"); exists {
f(p)
}
})
// Extract text from <p>, <a>, ... tags
simpleTags := []string{"p", "a", "h1", "h2", "h3", "h4", "h5", "h6", "button", "small", "label", "li"}
n := &goquery.Selection{}
for _, tag := range simpleTags {
n = n.Union(doc.Find(tag))
}
n.Each(func(i int, n *goquery.Selection) {
if n.Children().Length() == 0 {
f(n.Text())
}
})
return res
}
func extractTitleAndDescription(content string) (title, description string) {
idxStart := strings.Index(content, `{{define "title"}}`)
if idxStart >= 0 {
idxEnd := strings.Index(content, `{{end}}`)
if idxEnd >= 0 {
title = content[idxStart+18 : idxEnd]
content = content[idxEnd+7:]
}
}
idxStart = strings.Index(content, `{{define "description"}}`)
if idxStart >= 0 {
idxEnd := strings.Index(content, `{{end}}`)
if idxEnd >= 0 {
description = content[idxStart+24 : idxEnd]
}
}
return
}
func makeKey(text, prefix string) string {
text = strings.TrimSpace(text)
key := allowedCharsKeyRegex.ReplaceAllString(text, "")
key = strings.ToLower(key)
split := strings.SplitN(key, " ", keyWordLengthLimit+1)
limit := len(split)
if limit > keyWordLengthLimit {
limit = keyWordLengthLimit
}
key = strings.Join(split[:limit], "-")
key = fmt.Sprintf("%s-%s", prefix, key)
return key
}
func unique(lst []string) []string {
keys := make(map[string]struct{})
list := []string{}
for _, s := range lst {
if _, exist := keys[s]; !exist {
keys[s] = struct{}{}
list = append(list, s)
}
}
return list
}